This notebook describes the 106,229 hospitalized COVID-19 positive patients that were studied as part of the Consortium for Clinical Characterization of COVID-19 by EHR Neurology group.

library(tidyverse)
library(gridGraphics)
library(gridExtra)
library(ggpubr)
library(DT)
library(kableExtra)
library(cowplot)
library(RColorBrewer)
library(glue)
library(egg)
source("R/plot_theme.R")
source("R/demo_tables.R")
source("R/utils.R")

Import Data from each Healthcare system

# read in files from results folder 
# this folder contains all of the local healthcare system level analyses
rdas <- list.files(
  path = "results",
  pattern = ".rda",
  full.names = TRUE
)


for (rda in rdas) {
  load(rda)
}

rm(rdas, rda)

# create a list of participating healthcare systems from our study tracking spreadsheet
site_google_url <- "https://docs.google.com/spreadsheets/d/1epcYNd_0jCUMktOHf8mz5v651zy1JALD6PgzobrGWDY/edit?usp=sharing"

# load site parameters
site_params <- googlesheets4::read_sheet(site_google_url, sheet = 1)
site_avails <- googlesheets4::read_sheet(site_google_url, sheet = 2)

# filter the list of sites who ran the analysis
sorted_sites <- site_avails %>%
  filter(!is.na(date_v4_received)) %>%
  pull(siteid) %>%
  paste("results", sep = "_")

# list sites without race
sites_wo_race <- site_params %>%
  filter(!include_race) %>%
  pull(siteid)

# combine all rda files with 'results' in name
results <- mget(ls(pattern = "results"))

## load in the pre-processed comorbidity and meta-data
load("processed/comorbidity_table_cnps.rda")
load("processed/neuro_pt_counts.rda") 

Pre-processing

1. Format primary demographic characteristics

# create list of hospitals for adult and pediatric analyses
adult_sites = sorted_sites[!sorted_sites %in% c("BCH_results", "GOSH_results")]

pediatric_sites = sorted_sites[!sorted_sites %in% c("VA1_results", "VA2_results", "VA3_results", "VA4_results", "VA5_results")]

demo_table_adult <- create_demo_tableone(sorted_sites = adult_sites, is_pediatric = FALSE)

demo_table_pediatric <- create_demo_tableone(sorted_sites = pediatric_sites, is_pediatric = TRUE)

demo_table_combine <- rbind(demo_table_adult, demo_table_pediatric)

2. Format clinical characteristics (i.e: continuous variables such as median time to discharge)

clinical_table_adult <- create_clinical_tableone(sorted_sites = adult_sites, is_pediatric = FALSE)

clinical_table_pediatric <- create_clinical_tableone(sorted_sites = pediatric_sites, is_pediatric = TRUE)

clinical_table_combine <- rbind(clinical_table_adult, clinical_table_pediatric)

3. Conduct additional pre-processing of clinical variables

clinical_table_adult_clean <- clean_clinical_tables(clinical_table = clinical_table_adult)

clinical_table_pediatric_clean <- clean_clinical_tables(clinical_table = clinical_table_pediatric)

clinical_table_clean_combine <- rbind(clinical_table_adult_clean, clinical_table_pediatric_clean)

Add Comorbidity data

comorbidity_table_adults <- comorb_table_wide %>%
  filter(population == "Adult") %>% 
  arrange(desc(Comorb_Total)) %>% 
  slice(1:4) # return top comorbidities

comorbidity_table_pediatric <- comorb_table_wide %>%
  filter(population == "Pediatric") %>% 
  arrange(desc(Comorb_Total)) %>% 
  slice(1:4) # return top comorbidities

top_comorb_adults <- comorbidity_table_adults$Comorbidity
top_comorb_pediatrics <- comorbidity_table_pediatric$Comorbidity

Demographics

Table One - Combined

neuro_pt_counts_sum <- colSums(neuro_pt_counts[,-1]) %>% 
  data.frame() %>% 
  t() %>% 
  data.frame()

# specify the order of Table 1
row_order_adult <- c(
  "All Patients", "Female", "Male", "Unknown Sex",
  "0-2", "3-5", "6-11", "12-17", "18-25",
  "26-49", "50-69", "70-79", "80+", "Unknown Age",
  "American Indian", "Asian", "Black",
  "Hawaiian/Pacific Islander",
  "Hispanic/Latino", "White", "Other",
  top_comorb_adults, 
  "Median Elixhauser score  (sd) ",
  "Median pre admission cns  (sd) ",
  "Median pre admission pns  (sd) ",
  "Non-Severe", "Severe",
  "Median time to severe  (sd) ",
  "Alive", "Deceased",
  "Median time to death  (sd) ",
  "Discharged", "Not Discharged",
  "Median time to first discharge  (sd) ",
  "Not Readmitted", "Readmitted",
  "Median time to first readmission  (sd) ",
  "Median number of readmissions  (sd) "
  )


tableone_combine <- create_tableone(demo_table = demo_table_combine,
                                    clinical_table = clinical_table_clean_combine) %>%
  rbind(., comorb_table_wide %>%
          group_by(Comorbidity) %>%
          mutate(Comorb_Total = sum(Comorb_Total, na.rm = TRUE),
          None_sum = sum(None_Total, na.rm = TRUE),
          CNS_sum = sum(CNS_Total, na.rm = TRUE),
          PNS_sum = sum(PNS_Total, na.rm = TRUE),
          None_perc = round(None_sum/neuro_pt_counts_sum$None_n*100,1),
          Central_perc = round(CNS_sum/neuro_pt_counts_sum$CNS_n*100,1),
          Peripheral_perc = round(PNS_sum/neuro_pt_counts_sum$PNS_n*100,1),
          None = paste(None_sum, "(", None_perc, ")"),
          Central = paste(CNS_sum, "(", Central_perc, ")"),
          Peripheral = paste(PNS_sum, "(", Peripheral_perc, ")")) %>%
          distinct(Comorbidity, Comorb_Total, None, Central, Peripheral) %>%
          rename(`Table 1` = "Comorbidity",
          N = "Comorb_Total")) %>%
  slice(match(row_order_adult, `Table 1`))

kbl(tableone_combine %>%
      rename("No Neurological Condition (NNC)" = None)) %>%
      add_header_above(c(
      " ",
      "Total Patients" = 1,
      "Neurological Disease" = 3
      )) %>%
      kable_paper("striped", full_width = F) %>%
      pack_rows("Sex", 2, 4) %>%
      pack_rows("Age", 5, 13) %>%
      pack_rows("Race & Ethnicity", 14, 20) %>%
      pack_rows("Past Medical History", 21, 27) %>%
      pack_rows("Severity", 28, 30) %>%
      pack_rows("Survival", 31, 33) %>%
      pack_rows("Discharge", 34, 36) %>%
      pack_rows("Readmission", 37, 40)
Total Patients
Neurological Disease
Table 1 N No Neurological Condition (NNC) Central Peripheral
All Patients 106229 88340 (100%) 15101 (100%) 2788 (100%)
Sex
Female 37590 32399 (36.7%) 4247 (28.1%) 944 (33.9%)
Male 68638 55940 (63.3%) 10854 (71.9%) 1844 (66.1%)
Unknown Sex 1 1 (0%) 0 (0%) 0 (0%)
Age
0-2 769 747 (0.8%) 22 (0.1%) 0 (0%)
3-5 275 251 (0.3%) 24 (0.2%) 0 (0%)
6-11 403 368 (0.4%) 33 (0.2%) 2 (0.1%)
12-17 707 637 (0.7%) 60 (0.4%) 10 (0.4%)
18-25 2328 2138 (2.4%) 145 (1%) 45 (1.6%)
26-49 17799 16122 (18.3%) 1166 (7.7%) 511 (18.5%)
50-69 36885 31556 (35.7%) 4169 (27.7%) 1160 (41.9%)
70-79 24860 19672 (22.3%) 4508 (30%) 680 (24.6%)
80+ 22094 16813 (19%) 4920 (32.7%) 361 (13%)
Race & Ethnicity
American Indian 350 295 (0.3%) 55 (0.4%) 0 (0%)
Asian 1694 1464 (1.7%) 205 (1.4%) 25 (0.9%)
Black 16815 13368 (15.1%) 2995 (19.9%) 452 (16.5%)
Hawaiian/Pacific Islander 297 255 (0.3%) 41 (0.3%) 1 (0%)
Hispanic/Latino 870 819 (0.9%) 29 (0.2%) 22 (0.8%)
White 41871 33360 (37.8%) 7401 (49.1%) 1110 (40.5%)
Other 44220 38750 (43.9%) 4336 (28.8%) 1134 (41.3%)
Past Medical History
Hypertension 35587 27323 ( 30.9 ) 7277 ( 48.2 ) 987 ( 35.4 )
Alcohol abuse 30130 23065 ( 26.1 ) 6194 ( 41 ) 871 ( 31.2 )
Drug abuse 26596 20287 ( 23 ) 5528 ( 36.6 ) 781 ( 28 )
Diabetes 21969 16905 ( 19.1 ) 4429 ( 29.3 ) 635 ( 22.8 )
Median Elixhauser score (sd) 0.3 (0.7) 0.2 (0.5) 1.4 (2) 0.6 (1.7)
Median pre admission cns (sd) 0 (0) 0 (0) 0 (0) 0 (0)
Median pre admission pns (sd) 0 (0) 0 (0) 0 (0) 0 (0.1)
Severity
Non-Severe 66252 57771 (65.4%) 7007 (46.4%) 1474 (52.7%)
Severe 39985 30576 (34.6%) 8084 (53.6%) 1325 (47.3%)
Median time to severe (sd) 0.6 (1) 0.8 (1.3) 0.4 (0.7) 0.8 (1.1)
Survival
Alive 87376 74389 (84.2%) 10392 (68.8%) 2595 (93.1%)
Deceased 18849 13953 (15.8%) 4705 (31.2%) 191 (6.9%)
Median time to death (sd) 15.3 (6.4) 15.9 (5.8) 22.4 (32.5) 36.9 (41.4)
Discharge
Discharged 103165 85639 (96.9%) 14801 (98%) 2725 (97.7%)
Not Discharged 3063 2701 (3.1%) 297 (2%) 65 (2.3%)
Median time to first discharge (sd) 8.4 (4.2) 6.5 (3.6) 12.8 (6.1) 14.5 (18.3)
Readmission
Not Readmitted 91646 76877 (87%) 12435 (82.4%) 2334 (83.4%)
Readmitted 14569 11456 (13%) 2650 (17.6%) 463 (16.6%)
Median time to first readmission (sd) 28.1 (20.3) 26.4 (17.6) 31.9 (33.7) 38.1 (28.4)
Median number of readmissions (sd) 0.1 (0.3) 0 (0) 0.1 (0.3) 0.1 (0.3)
write.csv(tableone_combine, 'tables/table1.csv', row.names = FALSE)

Table One - Adults

tableone_adult <- create_tableone(demo_table = demo_table_adult,
                                  clinical_table = clinical_table_adult_clean) %>%
                                  rbind(., comorbidity_table_adults %>%
                                          select(Comorbidity, Comorb_Total, `NNC_N_%`, `CNS_N_%`, `PNS_N_%`) %>%
                                          rename(`Table 1` = "Comorbidity",
                                                 N = "Comorb_Total",
                                                 None = "NNC_N_%",
                                                 Central = "CNS_N_%",
                                                 Peripheral = "PNS_N_%")) %>% 
  slice(match(row_order_adult, `Table 1`))

kbl(tableone_adult %>%
    rename("No Neurological Condition (NNC)" = None)) %>%
  add_header_above(c(
  " ",
  "Total Patients" = 1,
  "Neurological Disease" = 3
  )) %>%
  kable_paper("striped", full_width = F) %>%
  pack_rows("Sex", 2, 4) %>%
  pack_rows("Age", 5, 9) %>%
  pack_rows("Race & Ethnicity", 10, 16) %>%
  pack_rows("Past Medical History", 17, 23) %>%
  pack_rows("Severity", 24, 26) %>%
  pack_rows("Survival", 27, 29) %>%
  pack_rows("Discharge", 30, 32) %>%
  pack_rows("Readmission", 33, 36)
Total Patients
Neurological Disease
Table 1 N No Neurological Condition (NNC) Central Peripheral
All Patients 104031 86321 (100%) 14938 (100%) 2772 (100%)
Sex
Female 36564 31445 (36.4%) 4185 (28%) 934 (33.7%)
Male 67466 54875 (63.6%) 10753 (72%) 1838 (66.3%)
Unknown Sex 1 1 (0%) 0 (0%) 0 (0%)
Age
18-25 2328 2138 (2.5%) 145 (1%) 45 (1.6%)
26-49 17799 16122 (18.7%) 1166 (7.8%) 511 (18.5%)
50-69 36885 31556 (36.6%) 4169 (28%) 1160 (42.1%)
70-79 24860 19672 (22.8%) 4508 (30.2%) 680 (24.7%)
80+ 22094 16813 (19.5%) 4920 (33%) 361 (13.1%)
Race & Ethnicity
American Indian 349 294 (0.3%) 55 (0.4%) 0 (0%)
Asian 1633 1412 (1.6%) 196 (1.3%) 25 (0.9%)
Black 16631 13202 (15.3%) 2979 (20%) 450 (16.5%)
Hawaiian/Pacific Islander 296 254 (0.3%) 41 (0.3%) 1 (0%)
Hispanic/Latino 850 799 (0.9%) 29 (0.2%) 22 (0.8%)
White 41206 32752 (38%) 7354 (49.3%) 1100 (40.3%)
Other 42971 37587 (43.6%) 4254 (28.5%) 1130 (41.4%)
Past Medical History
Hypertension 35566 27302 ( 31.6 %) 7277 ( 48.7 %) 987 ( 35.6 %)
Alcohol abuse 29918 22879 ( 26.5 %) 6169 ( 41.3 %) 870 ( 31.4 %)
Drug abuse 26389 20097 ( 23.3 %) 5512 ( 36.9 %) 780 ( 28.1 %)
Diabetes 21952 16888 ( 19.6 %) 4429 ( 29.6 %) 635 ( 22.9 %)
Median Elixhauser score (sd) 0.3 (0.7) 0.2 (0.5) 1.4 (2) 0.6 (1.7)
Median pre admission cns (sd) 0 (0) 0 (0) 0 (0) 0 (0)
Median pre admission pns (sd) 0 (0) 0 (0) 0 (0) 0 (0.1)
Severity
Non-Severe 64578 56181 (65.1%) 6935 (46.4%) 1462 (52.6%)
Severe 39468 30140 (34.9%) 8008 (53.6%) 1320 (47.4%)
Median time to severe (sd) 0.6 (1) 0.8 (1.3) 0.4 (0.7) 0.8 (1.1)
Survival
Alive 85212 72388 (83.9%) 10247 (68.6%) 2577 (93.1%)
Deceased 18820 13933 (16.1%) 4696 (31.4%) 191 (6.9%)
Median time to death (sd) 15.3 (6.4) 15.9 (5.8) 22.4 (32.5) 36.9 (41.4)
Discharge
Discharged 101032 83675 (96.9%) 14650 (98%) 2707 (97.7%)
Not Discharged 3005 2645 (3.1%) 295 (2%) 65 (2.3%)
Median time to first discharge (sd) 8.4 (4.2) 6.5 (3.6) 12.8 (6.1) 14.5 (18.3)
Readmission
Not Readmitted 89802 75170 (87.1%) 12312 (82.4%) 2320 (83.5%)
Readmitted 14237 11151 (12.9%) 2626 (17.6%) 460 (16.5%)
Median time to first readmission (sd) 28.1 (20.3) 26.4 (17.6) 31.9 (33.7) 38.1 (28.4)
Median number of readmissions (sd) 0.1 (0.3) 0 (0) 0.1 (0.3) 0.1 (0.3)

Table One - Pediatrics

# specify the order of Table 1
row_order_pediatric <- c(
  "All Patients", "Female", "Male", "Unknown Sex",
  "0-2", "3-5", "6-11", "12-17", "18-25",
  "26-49", "50-69", "70-79", "80+", "Unknown Age",
  "American Indian", "Asian", "Black",
  "Hawaiian/Pacific Islander",
  "Hispanic/Latino", "White", "Other",
  top_comorb_pediatrics, 
  "Median Elixhauser score  (sd) ",
  "Median pre admission cns  (sd) ",
  "Median pre admission pns  (sd) ",
  "Non-Severe", "Severe",
  "Median time to severe  (sd) ",
  "Alive", "Deceased",
  "Median time to death  (sd) ",
  "Discharged", "Not Discharged",
  "Median time to first discharge  (sd) ",
  "Not Readmitted", "Readmitted",
  "Median time to first readmission  (sd) ",
  "Median number of readmissions  (sd) "
  )

tableone_pediatric <- create_tableone(demo_table = demo_table_pediatric,
                                      clinical_table = clinical_table_pediatric_clean)  %>%
  rbind(., comorbidity_table_pediatric %>%
          select(Comorbidity, Comorb_Total, `NNC_N_%`, `CNS_N_%`, `PNS_N_%`) %>% 
          rename(`Table 1` = "Comorbidity",
                  N = "Comorb_Total",
                  None = "NNC_N_%",
                  Central = "CNS_N_%",
                  Peripheral = "PNS_N_%")) %>% 
  slice(match(row_order_pediatric, `Table 1`))

kbl(tableone_pediatric %>%
      rename("No Neurological Condition (NNC)" = None)) %>%
  add_header_above(c(
  " ",
  "Total Patients" = 1,
  "Neurological Disease" = 3
  )) %>%
  kable_paper("striped", full_width = F) %>%
  pack_rows("Sex", 2, 4) %>%
  pack_rows("Age", 5, 8) %>%
  pack_rows("Race & Ethnicity", 9, 15) %>%
  pack_rows("Past Medical History", 16, 22) %>%
  pack_rows("Severity", 23, 25) %>%
  pack_rows("Survival", 26, 28) %>%
  pack_rows("Discharge", 29, 31) %>%
  pack_rows("Readmission", 32, 35)
Total Patients
Neurological Disease
Table 1 N No Neurological Condition (NNC) Central Peripheral
All Patients 2198 2019 (100%) 163 (100%) 16 (100%)
Sex
Female 1026 954 (47.3%) 62 (38%) 10 (62.5%)
Male 1172 1065 (52.7%) 101 (62%) 6 (37.5%)
Unknown Sex 0 0 (0%) 0 (0%) 0 (0%)
Age
0-2 769 747 (37.3%) 22 (15.8%) 0 (0%)
3-5 275 251 (12.5%) 24 (17.3%) 0 (0%)
6-11 403 368 (18.4%) 33 (23.7%) 2 (16.7%)
12-17 707 637 (31.8%) 60 (43.2%) 10 (83.3%)
Race & Ethnicity
American Indian 1 1 (0%) 0 (0%) 0 (0%)
Asian 61 52 (2.6%) 9 (5.8%) 0 (0%)
Black 184 166 (8.3%) 16 (10.4%) 2 (12.5%)
Hawaiian/Pacific Islander 1 1 (0%) 0 (0%) 0 (0%)
Hispanic/Latino 20 20 (1%) 0 (0%) 0 (0%)
White 665 608 (30.2%) 47 (30.5%) 10 (62.5%)
Other 1249 1163 (57.8%) 82 (53.2%) 4 (25%)
Past Medical History
Alcohol abuse 212 186 ( 9.2 %) 25 ( 15.3 %) 1 ( 6.2 %)
Drug abuse 207 190 ( 9.4 %) 16 ( 9.8 %) 1 ( 6.2 %)
Weight loss 123 109 ( 5.4 %) 13 ( 8 %) 1 ( 6.2 %)
Cardiac arrhythmias 109 94 ( 4.7 %) 12 ( 7.4 %) 3 ( 18.8 %)
Median Elixhauser score (sd) 2.2 (4.5) 0 (0) 4.4 (9.2) 2.5 (3.6)
Median pre admission cns (sd) 0.2 (0.7) 0.1 (0.2) 1.9 (3.9) 0 (0)
Median pre admission pns (sd) 0 (0) 0 (0) 0 (0) 0.2 (0.6)
Severity
Non-Severe 1674 1590 (78.5%) 72 (48.6%) 12 (70.6%)
Severe 517 436 (21.5%) 76 (51.4%) 5 (29.4%)
Median time to severe (sd) 2.4 (5.3) 3.8 (9.5) 10.8 (33.1) 0.9 (2.5)
Survival
Alive 2164 2001 (99%) 145 (94.2%) 18 (100%)
Deceased 29 20 (1%) 9 (5.8%) 0 (0%)
Median time to death (sd) 12.6 (36.3) 53.5 (76.3) 5.8 (10.2) 95 (268.7)
Discharge
Discharged 2133 1964 (97.2%) 151 (98.7%) 18 (100%)
Not Discharged 58 56 (2.8%) 2 (1.3%) 0 (0%)
Median time to first discharge (sd) 5.1 (4.2) 3.8 (4.2) 5.8 (2.7) 5.9 (4.5)
Readmission
Not Readmitted 1844 1707 (84.8%) 123 (83.7%) 14 (82.4%)
Readmitted 332 305 (15.2%) 24 (16.3%) 3 (17.6%)
Median time to first readmission (sd) 20 (20.2) 20.1 (19) 75.3 (86) 14.4 (22.8)
Median number of readmissions (sd) 0.1 (0.3) 0 (0) 0.2 (0.4) 0.4 (0.5)

Sample Size

n_adult <- as.numeric(tableone_adult$N)[1]
n_pediatric <- as.numeric(tableone_pediatric$N)[1]

Demographic & Clinical Course Analysis

Evaluate characteristics among neuro status groups

1. Conduct chi-squared tests for categorical demographic variables

compute_chi_square <- function(tableone, demo_table) {
  
  N = tableone %>% select(N) %>% head(1) %>% as.numeric()

  # sum of all neuro condition groups
  cns_n = as.numeric(gsub( " .*$", "", tableone[1,"Central"]))
  pns_n = as.numeric(gsub( " .*$", "", tableone[1,"Peripheral"]))
  none_n = as.numeric(gsub( " .*$", "", tableone[1,"None"]))
  
  # sum up the total counts of each demographic variable across healthcare systems
  tableOne_sums <- demo_table %>%
    group_by(variable, Demo_var, Demo_var_i) %>%
    summarise(across(
      starts_with("n_var"),
      function(x) sum(x, na.rm = TRUE)
    ), .groups = "drop")
  
  # create list of demographic/clinical variables
  vars = tableOne_sums$variable
  
  # for binary variables, we should group by category 
  # Thus, only run the analysis for sex.male rather than both sex.female AND sex.male which would be redundant
  # we will also remove `age_group.unknown` since their is only 1 patient here
  exclude <- c("readmitted.false", "sex.female", "sex.other", "survival.deceased", "severity.non-severe",
               "covid_discharged.discharged", "age_group.Unknown")
  vars <- vars[!vars %in% exclude]
  
  # create empty list for chi.square test results
  chi_result_list = list()
  
  # for each variable, we will run the chi.square test
  for(i in vars) {
    test = calc_chisq(i, tableOne_sums, none_n, pns_n, cns_n)
    X2 <- round(test$statistic, 4)
    p_value <- test$p.value
    df <- test$parameter
    Variable = paste(i)
    
    chi_results <- cbind(Variable, X2, p_value, df) %>% 
      data.frame() %>% 
      mutate(p_value = as.numeric(p_value))
    
    rownames(chi_results) <- NULL
    
    chi_result_list[[i]] <- chi_results
  }
  
  # save list of chi.square test results
  chisq_results <- bind_rows(chi_result_list)
  
  return(chisq_results)
  
  
  }

Combined Adult & Pediatric

chisq_combine <- compute_chi_square(tableone = tableone_combine %>%
                                      filter(!`Table 1` == 'Discharged',
                                             !`Table 1` == 'Not Discharged'),
                                    demo_table = demo_table_combine %>%
                                      filter(!variable == 'covid_discharged.discharged',
                                             !variable == 'covid_discharged.not discharged'))

datatable(chisq_combine %>% arrange(p_value))

Adult

chisq_adult <- compute_chi_square(tableone = tableone_adult %>%
                                      filter(!`Table 1` == 'Discharged',
                                             !`Table 1` == 'Not Discharged'),
                                    demo_table = demo_table_adult %>%
                                      filter(!variable == 'covid_discharged.discharged',
                                             !variable == 'covid_discharged.not discharged'))

datatable(chisq_adult %>% arrange(p_value))

Pediatric

chisq_pediatric <- compute_chi_square(tableone = tableone_pediatric %>%
                                      filter(!`Table 1` == 'Discharged',
                                             !`Table 1` == 'Not Discharged'),
                                    demo_table = demo_table_pediatric %>%
                                      filter(!variable == 'covid_discharged.discharged',
                                             !variable == 'covid_discharged.not discharged'))
## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect

## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect

## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect

## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect

## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect

## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect

## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect

## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect

## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect

## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect

## Warning in chisq.test(unlist(mat)): Chi-squared approximation may be incorrect
datatable(chisq_pediatric %>% arrange(p_value))

2. Conduct anova (Kruskal-Wallis) tests for continuous clinical variables

compute_kruskal <- function(clinical_table) {

  # create empty list to save processed variables
  processed_list_figs <- list()
  
  # create a list of clinical variables to further pre-process
  vars_to_process <- unique(clinical_table$name)
  
  # for each variable, we will remove the [min, max] as before
  for (i in vars_to_process) {
    mod_table <- clinical_table %>%
      rename("var" = name) %>%
      filter(var == i) %>%
      mutate(
        site = toupper(site),
        None = sub("(\\(.*|\\[.*)", "", None),
        Peripheral = sub("(\\(.*|\\[.*)", "", Peripheral),
        Central = sub("(\\(.*|\\[.*)", "", Central)
      ) %>%
      mutate(across(None:Central, as.numeric))
  
    processed_list_figs[[i]] <- mod_table
  }
  
  clinical_table_anova <- bind_rows(processed_list_figs)
  
  # format the continuous variables
  cont_vars = clinical_table_anova %>%
    mutate(
      # create a variable without mean/median prefix
      grouped_var = gsub("Mean |Median | \\[Min, Max\\]| \\(SD\\)", "", var)
    ) %>%
    pivot_longer(
      cols = None:Central,
      names_to = "type"
    ) %>%
    mutate(type = factor(type, levels = c("None", "Central", "Peripheral"))) %>% 
    # select variables to analyze
    filter(grouped_var %in% c("Elixhauser score",
                              "pre admission cns", 
                              "pre admission pns", 
                              "time to death", 
                              "time to first discharge", 
                              "time to severe",
                              "number of readmissions",
                              "time to first readmission")) %>% 
    distinct()
  
  # create function to conduct anova
  cont_var_results <- function(variable) {
    
    df <- cont_vars %>% 
    filter(grouped_var == paste(variable))
    
    # kruskal.test calculates the kruskal-Wallis H-statistic 
    # does not assume normality between groups
    # also called the one-way ANOVA on ranks
    test = kruskal.test(df$value ~ df$type)
    
  }
  
  # create empty list for Kruskal-Wallis results
  cont_vars_list = list()
  
  # create list of unique variables
  outcome_cont_vars = unique(cont_vars$grouped_var)
  
  for(i in outcome_cont_vars) {
    
    test = cont_var_results(i)
    X2 <- round(test$statistic, 4)
    p_value <- test$p.value
    df <- test$parameter
    Variable = paste(i)
    
    kw <- cbind(Variable, X2, p_value, df) %>% 
      data.frame() 
    
    rownames(kw) <- NULL
    
    cont_vars_list[[i]] <- kw
  
    
  }
  
  anova_results <- bind_rows(cont_vars_list)
  
  }

Adult & Pediatric Kruskall-Wallis

kw_combine <- compute_kruskal(clinical_table = clinical_table_combine)
datatable(kw_combine %>% arrange(p_value))

Adult Clinical Kruskall-Wallis

Note: median CNS is 0 for all sites (reason for the NA values)

kw_adult <- compute_kruskal(clinical_table = clinical_table_adult)
datatable(kw_adult %>% arrange(p_value))

Pediatric Kruskall-Wallis

kw_pediatric <- compute_kruskal(clinical_table = clinical_table_pediatric)
datatable(kw_pediatric %>% arrange(p_value))

3. Combine chi-square and Kruskal-Wallis results

We will evaluate significance when controlling for False Discovery Rate (FDR)

tableone_stats <- function(chisq_results, anova_results, population) {

  demo_stats <- rbind(chisq_results %>% select(Variable, p_value),
                      anova_results %>% select(Variable, p_value))
  
  # adjust p-values with FDR
  demo_stats$adj_p_value <- p.adjust(demo_stats$p_value, method = "fdr")
  
  # round & format p-value 
  demo_stats <- demo_stats %>% 
    mutate(p_value = as.numeric(p_value),
           pvalue = if_else(p_value < 0.001, "< 0.001", paste(round(p_value, 3))),
           adj.p_value = if_else(adj_p_value < 0.001, "< 0.001", paste(round(adj_p_value, 3))))
  
  # tidy up the demographics stat table
  demo_stats_tidy <- demo_stats %>% 
    rename(`Unadjusted P-value (raw)` = p_value,
           `FDR Adjusted P-value (raw)` = adj_p_value,
           `Unadjusted P-value (tidy)` = pvalue,
           `FDR Adjusted P-value (tidy)` = adj.p_value) %>% 
    select(Variable, `Unadjusted P-value (raw)`, `FDR Adjusted P-value (raw)`, `Unadjusted P-value (tidy)`, `FDR Adjusted P-value (tidy)`)

  write.csv(demo_stats_tidy, paste0("tables/Table1_pvals_", population, ".csv"), row.names = FALSE)
  
  return(demo_stats_tidy)
  
}

Incorporate Comorbidities

# define matrix for each comorbidity
comorb_matrix <- function(comorbidity_table, comorbidity, population) {

    if(population=='Combined') {
      comorbidity_table = comorbidity_table
    } else if (population=="Adult") {
      comorbidity_table = comorbidity_table %>% 
        filter(population == "Adult")
    } else if (population=="Pediatric") {
      comorbidity_table = comorbidity_table %>% 
        filter(population == "Pediatric")
    } else {
      print('incorrect population specification')
    }
  
  mat_comorb <- comorbidity_table %>%
    filter(Comorbidity == paste(comorbidity)) %>%
    group_by(Comorbidity) %>% 
    mutate(None_comorb = sum(None_Total, na.rm = TRUE),
    CNS_comorb = sum(CNS_Total, na.rm = TRUE),
    PNS_comorb = sum(PNS_Total, na.rm = TRUE)) %>%
    ungroup() %>%
    distinct(None_comorb, CNS_comorb, PNS_comorb) %>%
    mutate(none_dif = neuro_pt_counts_sum$None_n - None_comorb,
    pns_dif = neuro_pt_counts_sum$PNS_n - PNS_comorb,
    cns_dif = neuro_pt_counts_sum$CNS_n - CNS_comorb) %>%
    data.frame() %>%
    #as.integer() %>%
    matrix(nrow = 2, ncol = 3, byrow = TRUE)

  comorb_results <- chisq.test(unlist(mat_comorb))
  
  results_df <- data.frame(Variable = paste(comorbidity),
                                X2 = round(comorb_results$statistic, 4),
                                p_value = comorb_results$p.value,
                                df = comorb_results$parameter)
  row.names(results_df) <- NULL
  
  return(results_df)
}

## combined comorbidities
comorbidity_combined_chi_list <- list()

for(i in top_comorb_adults) {
  
  comorb_chi_results <- comorb_matrix(comorbidity_table = comorb_table_wide, i, population = "Combined")
  comorbidity_combined_chi_list[[i]] <- comorb_chi_results
}

comorbidity_combined_chi <- comorbidity_combined_chi_list %>%
  bind_rows() 

chisq_combine_comorb <- chisq_combine %>%
  mutate(X2 = as.numeric(X2),
  df = as.numeric(df)) %>%
  bind_rows(comorbidity_combined_chi) 



## adult comorbidities
comorbidity_adult_chi_list <- list()

for(i in top_comorb_adults) {
  
  comorb_chi_results <- comorb_matrix(comorbidity_table = comorb_table_wide, i, population = "Adult")
  comorbidity_adult_chi_list[[i]] <- comorb_chi_results
}

comorbidity_adult_chi <- comorbidity_adult_chi_list %>%
  bind_rows() 

chisq_adult_comorb <- chisq_adult %>%
  mutate(X2 = as.numeric(X2),
  df = as.numeric(df)) %>%
  bind_rows(comorbidity_adult_chi) 

## pediatric comorbidities
comorbidity_pediatric_chi_list <- list()

for(i in top_comorb_pediatrics) {
  
  comorb_chi_results <- comorb_matrix(comorbidity_table = comorb_table_wide, i, population = "Pediatric")
  comorbidity_pediatric_chi_list[[i]] <- comorb_chi_results
}

comorbidity_pediatric_chi <- comorbidity_pediatric_chi_list %>%
  bind_rows() 

chisq_pediatric_comorb <- chisq_pediatric %>%
  mutate(X2 = as.numeric(X2),
  df = as.numeric(df)) %>%
  bind_rows(comorbidity_pediatric_chi_list) 

Table One - Combined Analysis

combine_tableone_stats <- tableone_stats(chisq_results = chisq_combine_comorb,
                                         anova_results = kw_combine,
                                         population = "combined") 
  

datatable(combine_tableone_stats)

Table One - Adult Analysis

adult_tableone_stats <- tableone_stats(chisq_results = chisq_adult_comorb,
                                         anova_results = kw_adult,
                                         population = "adult") 
  

datatable(adult_tableone_stats)

Table One - Pediatric Analysis

pediatric_tableone_stats <- tableone_stats(chisq_results = chisq_pediatric_comorb,
                                         anova_results = kw_pediatric,
                                         population = "pediatric") 
  

datatable(pediatric_tableone_stats)

Neurological Diagnosis Analysis

Evaluate CNS and PNS neurological diagnoses in our patient cohort

Format Diagnostic code data

# import list of ICD-9/ICD-10 codes
neuro_icds_10 <-
  readxl::read_excel("public-data/2020-02-22_neuro-icd10_CNSvPNS.xlsx", sheet = 2) %>% 
  rename(
    "icd" = `ICD-10`,
    "pns_cns" = `Nervous system Involvement (1=central, 2=peripheral, 3=irrelevant)`,
    "type" = `ICD-10_type (1=first three alphanumeric code only, 2=digits after decimal point)`) %>%
  filter(type == 1) %>% 
  mutate(pns_cns = as.factor(pns_cns) %>% fct_recode(
    Central = "1",
    Peripheral = "2"
  )) %>%
  distinct(icd, `Neurological Disease Category`, pns_cns, `ICD-10 Description Revised`) %>%
  rename("description" = `ICD-10 Description Revised`) %>%
  mutate(concept_type = "icd-10")

neuro_icds_9 <- read.csv("public-data/icd9_tab_CNSvPNS.csv") %>%
  rename(
    "Neurological Disease Category" = "Neurological.Disease.Category",
    "pns_cns" = `Nervous.system.Involvement..1.central..2.peripheral.`,
    "icd_description" = `icd9_desc_revised`
  ) %>%
  mutate(
    pns_cns = as.factor(pns_cns) %>% fct_recode(
      Central = "1",
      Peripheral = "2"
    ),
    concept_type = "DIAG-ICD9"
  ) %>%
  select(icd, `Neurological Disease Category`, pns_cns, icd_description) %>%
  rename("description" = icd_description) %>%
  mutate(concept_type = "icd-9")

# bind list of ICD-9/ICD-10 codes
icds <- rbind(neuro_icds_10, neuro_icds_9)

Calculate total number of neuro diagnoses for each adults & pediatrics

compute_neuro_counts <- function(sorted_sites, sample_size, is_pediatric = FALSE) {
  
    # create empty list to save processed diagnostic data
    diag_table_list <- list()
  
    if(is_pediatric==FALSE) {
      population = "adults"
      } else {
      population = "pediatrics"
      }
  
  # for each healthcare system, we will calculate the total number of patients with each ICD code
  for (i in sorted_sites) {
    tmp <- get(i)
    tmp_diag <- tmp[[c(
      "first_hosp_results",
      "icd_tables",
      paste0("icd_tables_", population),
      "demo_table"
    )]]
    tmp_diag$site <- tmp[["site"]]
    diag_table_list[[i]] <- tmp_diag %>%
      filter(variable == "sex.female" | variable == "sex.male" | variable == "sex.other") %>%
      select(site, contains("n_var"))
  }
  
  diag_table_wide <- bind_rows(diag_table_list)
  
  # we will count the total number and percent across healthcare systems
  diag_counts <- colSums(Filter(is.numeric, diag_table_wide), na.rm = TRUE) %>% 
    data.frame() %>% 
    mutate(perc = round(./sample_size * 100, 1))
  
  # format diagnostic table
  diag_counts <- tibble::rownames_to_column(diag_counts, "icd") %>%
    mutate(icd = gsub("n_var_", "", icd)) %>%
    rename(`Number of Patients` = ".") %>%
    full_join(., icds, by = "icd") %>%
    dplyr::arrange(desc(`Number of Patients`)) %>% 
    mutate(`Number of Patients (% of Cohort)` = paste(`Number of Patients`, "(", perc, ")", sep = " ")) %>% 
    select(pns_cns, `Neurological Disease Category`, concept_type, 
           icd, description, `Number of Patients (% of Cohort)`) %>% 
      rename(Code = "icd",
           `Nervous System` = "pns_cns",
           Description = "description",
           `ICD Version` = "concept_type") %>% 
    mutate(`Number of Patients (% of Cohort)` = if_else(`Number of Patients (% of Cohort)` == "NA ( NA )", "0 ( 0 )", `Number of Patients (% of Cohort)`),
           Code = if_else(Code == "NN", "No Neurological Disease (NNC)", Code))
  
  return(diag_counts)
  
  }
neuro_counts_adult <- compute_neuro_counts(sorted_sites = adult_sites,
                                           sample_size = n_adult, 
                                           is_pediatric = FALSE)

neuro_counts_pediatric <- compute_neuro_counts(sorted_sites = pediatric_sites,
                                           sample_size = n_pediatric, 
                                           is_pediatric = TRUE)

neuro_counts_combined <- neuro_counts_adult %>% 
  rename(`Number of Adult Patients (% of Cohort)` = `Number of Patients (% of Cohort)`) %>% 
  left_join(., neuro_counts_pediatric %>% 
              select(`ICD Version`, Code, `Number of Patients (% of Cohort)`) %>% 
              rename("Number of Pediatric Patients (% of Cohort`)" = `Number of Patients (% of Cohort)`))
## Joining, by = c("ICD Version", "Code")
write.csv(neuro_counts_combined, paste0("tables/Supp.Table2_diagnoses", ".csv"), row.names = FALSE)
datatable(neuro_counts_adult, caption = "Number (%) of Adult Patients with each Neurological Diagnosis") 
datatable(neuro_counts_pediatric, caption = "Number (%) of Pediatric Patients with each Neurological Diagnosis") 

Evaluate Diagnoses by Age & Outcome

compute_stratified_neuro_counts <- function(tableone, sorted_sites, is_pediatric = FALSE) {
  
  # create empty list to save processed diagnostic data
    diag_table_list <- list()
  
    if(is_pediatric==FALSE) {
      population = "adults"
      } else {
      population = "pediatrics"
      }
  
  # for each healthcare system, we will calculate the total number of patients with each ICD code
  for (i in sorted_sites) {
    #print(i)
    tmp <- get(i)
    tmp_diag <- tmp[[c(
      "first_hosp_results",
      "icd_tables",
      paste0("icd_tables_", population),
      "demo_table"
    )]]
    tmp_diag$site <- tmp[["site"]]
    try(
    diag_table_list[[i]] <- tmp_diag %>%
      select(site, variable, starts_with("n_var")) %>% 
      pivot_longer(cols = starts_with("n_var"), names_to = "code", values_to = "freq") %>% 
      ungroup()
    )
  }

  diag_table_wide_all <- bind_rows(diag_table_list) %>% 
      group_by(variable, code) %>% 
    # total number of patients who had each code for each variable across sites
      mutate(total_n_var  = sum(freq, na.rm = TRUE)) %>% 
    distinct(variable, code, total_n_var) %>% 
    mutate(code = gsub("n_var_", "", code),
           variable = gsub("Severity.", "", variable),
           variable = gsub("Survival.", "", variable),
           variable = gsub("readmitted", "", variable),
           variable = gsub("age_group", "", variable),
           variable = gsub("covid_discharged", "", variable),
           variable = if_else(variable == ".TRUE", "Readmitted", variable)) %>% 
    left_join(., icds %>% rename(code = "icd"), by = "code") %>% 
    ungroup() %>% 
    filter(!concept_type == "icd-9")


  # refactor neuro status
  diag_table_wide_all$pns_cns <- factor(diag_table_wide_all$pns_cns,
                                        levels=c("Central", "Peripheral"),
                                        labels=c("CNS", "PNS"))
  
  
  return(diag_table_wide_all)
  
  }
stratified_neuro_counts_adult <- compute_stratified_neuro_counts(sorted_sites = adult_sites,
                                                                 is_pediatric = FALSE,
                                                                 tableone = tableone_adult)

stratified_neuro_counts_pediatric <- compute_stratified_neuro_counts(sorted_sites = pediatric_sites,
                                                                 is_pediatric = TRUE,
                                                                 tableone = tableone_pediatric)
compute_stratified_neuro_counts_age <- function(stratified_neuro_counts, tableone) {
  
  stratified_neuro_counts$variable <- factor(stratified_neuro_counts$variable, 
                                     levels=c(".00to02", ".03to05", ".06to11", ".12to17",
                                              ".18to25", ".26to49", ".50to69", ".70to79", ".80plus"), 
                                     labels=c("0-2 Years", "3-5 Years", "6-11 Years", "12-17 Years",
                                              "18-25 Years", "26-49 Years", "50-69 Years", "70-79 Years", "80+ Years"))
  
  # total counts
  n_0_2 <- get_table_n(tableone, var = "0-2")
  n_3_5 = get_table_n(tableone, var = "3-5")
  n_6_11 = get_table_n(tableone, var = "6-11")
  n_12_17 = get_table_n(tableone, var = "12-17")
  n_18_25 = get_table_n(tableone, var = "18-25")
  n_26_49 = get_table_n(tableone, var = "26-49")
  n_50_69 = get_table_n(tableone, var = "50-69")
  n_70_79 = get_table_n(tableone, var = "70-79")
  n_80 = get_table_n(tableone, var = "80+")
  
  age_df <- data.frame("variable" = c("0-2 Years",
                                    "3-5 Years", 
                                    "6-11 Years", 
                                    "12-17 Years",
                                    "18-25 Years", 
                                    "26-49 Years", 
                                    "50-69 Years", 
                                    "70-79 Years", 
                                    "80+ Years"),
                     "n_var" = c(n_0_2,
                               n_3_5,
                               n_6_11,
                               n_12_17,
                               n_18_25,
                               n_26_49,
                               n_50_69,
                               n_70_79,
                               n_80))

  
  stratified_neuro_counts <- stratified_neuro_counts %>% 
    filter(grepl("Years", variable),
                !code == "NN", 
                !total_n_var == 0) %>% 
         # total_n would be calculating total codes across each age group which is not true reflection of patient counts b/c patients can have more than one code
         #mutate(total_n = sum(total_n_var)) %>% 
         ungroup() %>% 
         left_join(., age_df, by = "variable") %>% 
         mutate(n_var = as.numeric(n_var),
                perc = as.character(round(total_n_var/n_var*100,1)),
                perc = if_else(perc < 0.1, '<0.1', perc),
                perc = paste0(perc, "%")) 
  
  return(stratified_neuro_counts)
  
  
}
stratified_neuro_counts_age_adult <- compute_stratified_neuro_counts_age(stratified_neuro_counts = stratified_neuro_counts_adult,
                                                                         tableone = tableone_adult)

stratified_neuro_counts_age_pediatric <- compute_stratified_neuro_counts_age(stratified_neuro_counts = stratified_neuro_counts_pediatric,
                                                                 tableone = tableone_pediatric)

Diagnoses by Adult and Pediatric Populations

n_0_2 <- get_table_n(tableone_combine, var = "0-2")
#n_3_5 <- get_table_n(tableone_combine, var = "3-5")
n_6_11 <- get_table_n(tableone_combine, var = "6-11")
n_12_17 <- get_table_n(tableone_combine, var = "12-17")

total_ped_age_count = as.numeric(n_0_2) +  as.numeric(n_6_11) + as.numeric(n_12_17)

stratified_neuro_counts_age_pediatric_reformat = stratified_neuro_counts_age_pediatric %>%
  select(variable, pns_cns, description, n_var, total_n_var) %>%
  mutate(variable = "0-17 Years") %>%
  group_by(pns_cns, description) %>%
  mutate(total_n_var = sum(total_n_var, na.rm = TRUE)) %>%
  ungroup() %>%
  # n_var is the total per `age_group` stratification
  select(-n_var) %>%
  distinct() %>%
  mutate(perc = round(total_n_var/total_ped_age_count*100,1),
         perc = if_else(perc < 0.1, '<0.1', as.character(perc)),
         perc = paste0(perc, "%"))

stratified_neuro_counts_age_combined <- stratified_neuro_counts_age_adult %>%
  select(variable, pns_cns, description, total_n_var, perc) %>%
  rbind(., stratified_neuro_counts_age_pediatric_reformat %>%
          select(variable, pns_cns, description, total_n_var, perc))

stratified_neuro_counts_age_combined$variable <- factor(stratified_neuro_counts_age_combined$variable,
                                                                      levels=c("0-17 Years", "18-25 Years", "26-49 Years", "50-69 Years", "70-79 Years", "80+ Years"))
group.colors <- c(NNC = "gray", PNS = "slateblue", CNS ="tomato")

adult_ped_combined_plot <- ggplot(stratified_neuro_counts_age_combined,
                                                aes(x = total_n_var, y = reorder(description,
                                                                                 total_n_var), 
                                                    fill = pns_cns)) +
  geom_bar(stat = "identity") +
  geom_text(
    aes(x = total_n_var, y = description, label = perc),
    hjust = -0.1,
    #hjust="inward",
    size = 6,
    inherit.aes = TRUE) +
  scale_x_continuous(expand = expansion(mult = c(0,0.2))) + # to ensure percent labels are within the figure
  facet_grid(pns_cns ~ variable, scales = "free", space = "free_y") + 
  scale_y_discrete("", position="left", labels = function(x) str_wrap(x, width = 100)) +   
  scale_fill_manual(values = group.colors) +
  xlab("Number of Patients") +
  theme(legend.position = "none",
        strip.text.y = element_text(size = 40),
        strip.text.x = element_text(size = 35),
        axis.text.y.left = element_text(size = 25, face = "bold"),
        axis.text.x = element_text(size = 25),
        axis.title.x = element_text(size = 35))

ggsave("figures/Fig3_diagnoses_by_age_adult_ped_18group.png", adult_ped_combined_plot, width = 40, height = 19, dpi = 300)

Evaluate Diagnoses by Outcome

stratified_neuro_counts_adult$population = "adult"
stratified_neuro_counts_pediatric$population = "pediatric"

stratified_neuro_counts_combine <- rbind(stratified_neuro_counts_adult, stratified_neuro_counts_pediatric)

## determine total number of patients who died or severe
n_death_adult = get_table_n(df = tableone_adult, var = "Deceased") %>% as.numeric()
n_severe_adult = get_table_n(df = tableone_adult, var = "Severe") %>% as.numeric()

n_death_pediatric = get_table_n(df = tableone_pediatric, var = "Deceased") %>% as.numeric()
n_severe_pediatric = get_table_n(df = tableone_pediatric, var = "Severe") %>% as.numeric()

stratified_neuro_counts_outcomes <- stratified_neuro_counts_combine %>% 
  filter(variable %in% c("Severe", "Deceased"),
         !code == "NN", 
         !total_n_var == 0) %>%          
         # total_n would be calculating total codes across each outcome group which is not true reflection of patient counts b/c patients can have more than one code
         #mutate(total_n = sum(total_n_var)) %>% 
         group_by(variable, description, population) %>% 
  mutate(total_n_var = sum(total_n_var)) %>% 
  ungroup() %>% 
  mutate(perc = case_when(variable == "Severe" & population == "adult" ~ round(total_n_var/n_severe_adult*100,1),
                                 variable == "Deceased" & population == "adult" ~ round(total_n_var/n_death_adult*100,1),
                                 variable == "Severe" & population == "pediatric" ~ round(total_n_var/n_severe_pediatric*100,1),
                                 variable == "Deceased" & population == "pediatric" ~ round(total_n_var/n_death_pediatric*100,1)),
         perc = if_else(perc < 0.1, '<0.1', as.character(perc)),
       perc = paste0(perc, "%")) %>% 
  select(-code, -concept_type) %>% 
  distinct()
adult_outcomes = ggplot(stratified_neuro_counts_outcomes %>% 
         filter(population == "adult") %>% 
           mutate(variable = as.factor(variable) %>% 
                                          fct_recode(
                                            Mortality = "Deceased")),
       aes(x = total_n_var, y = reorder(description, total_n_var), fill = pns_cns)) +
  geom_bar(stat = "identity") +
 geom_text(
    aes(x = total_n_var, y = description, label = perc),
    hjust = -0.1,
    #hjust="inward",
    size = 5,
    inherit.aes = TRUE) +
  scale_x_continuous(expand = expansion(mult = c(0,0.2))) + 
  facet_grid(pns_cns ~ variable, scales = "free", space = "free_y") + 
  scale_y_discrete("", position="left", labels = function(x) str_wrap(x, width = 100)) +   
  scale_fill_manual(values = group.colors) +
  xlab("Number of Patients") + 
    theme(legend.position = "none",
        strip.text.y = element_text(size = 35),
        strip.text.x = element_text(size = 30),
        axis.text.y.left = element_text(size = 20, face = "bold"),
        axis.text.x = element_text(size = 15),
        axis.title.x = element_text(size = 35))

ggsave("figures/SuppFig1_diagnoses_by_outcomes_adult.png", adult_outcomes, width = 25, height = 15, dpi = 300)
pediatric_outcomes = ggplot(stratified_neuro_counts_outcomes %>% 
         filter(population == "pediatric") %>% 
           mutate(variable = as.factor(variable) %>% 
                                          fct_recode(
                                            Mortality = "Deceased")),
       aes(x = total_n_var, y = reorder(description, total_n_var), fill = pns_cns)) +
  geom_bar(stat = "identity") +
 geom_text(
    aes(x = total_n_var, y = description, label = perc),
    hjust = -0.1,
    #hjust="inward",
    size = 5,
    inherit.aes = TRUE) +
  scale_x_continuous(expand = expansion(mult = c(0,0.2))) + 
  facet_grid(pns_cns ~ variable, scales = "free") + 
  scale_y_discrete("", position="left", labels = function(x) str_wrap(x, width = 100)) +   
  scale_fill_manual(values = group.colors) +
  xlab("") + 
  theme(legend.position = "none",
        strip.text.y = element_text(size = 35),
        strip.text.x = element_text(size = 30),
        axis.text.y.left = element_text(size = 20, face = "bold"),
        axis.text.x = element_text(size = 15),
        axis.title.x = element_text(size = 35))

ggsave("figures/SuppFig1_diagnoses_by_outcomes_pediatric.png", pediatric_outcomes, width = 25, height = 9, dpi = 300)
#grid.arrange(pediatric_outcomes, adult_outcomes, ncol=1)

ggsave("figures/SuppFig1_diagnoses_by_outcome_combined.png", grid.arrange(pediatric_outcomes, adult_outcomes, ncol=1), width = 25, height = 25, dpi = 300)

Evaluate patients with “Both” CNS and PNS diseases

These patients were excluded from the primary analysis due to potential confounding.

# create empty list to store the counts of "both" patients
both_counts_list <- list()

# for each healthcare system, retrieve the number of excluded "both" patients recorded
for (i in sorted_sites) {
  n_both <- results[[paste(i)]][["first_hosp_results"]][["both_counts"]]
  
  both_counts_list[[i]] <- n_both
}

both_counts <- bind_rows(both_counts_list) %>% t() %>% data.frame() %>% sum()

print(paste(both_counts, "(", round(both_counts/(as.numeric(tableone_combine$N[1])+both_counts)*100,2), "%)")) 
## [1] "1990 ( 1.84 %)"

Comorbidity Analysis

Create comorbidity count table

comorbidity_table_adult <- comorb_table_wide %>%
  select(Comorbidity, population, Comorb_Total, `NNC_N_%`, `CNS_N_%`, `PNS_N_%`) %>%
  filter(population == "Adult") %>% 
  rename(N = "Comorb_Total",
         NNC = "NNC_N_%",
         CNS = "CNS_N_%",
         PNS = "PNS_N_%") %>% 
  mutate(N = paste0(N, " (", round(N/n_adult, 2)*100, "%)"))

comorbidity_table_pediatric <- comorb_table_wide %>%
  select(Comorbidity, population, Comorb_Total, `NNC_N_%`, `CNS_N_%`, `PNS_N_%`) %>%
  filter(population == "Pediatric") %>% 
  rename(N = "Comorb_Total",
         NNC = "NNC_N_%",
         CNS = "CNS_N_%",
         PNS = "PNS_N_%") %>% 
  mutate(N = paste0(N, " (", round(N/n_pediatric, 2)*100, "%)"))

supp_comorbidity_table <- rbind(comorbidity_table_adult, comorbidity_table_pediatric) %>% 
  arrange(Comorbidity)

write.csv(supp_comorbidity_table, "tables/SuppTable_Comorbidity_Counts.csv", row.names = FALSE)

Create vector counts of patients with comorbidity data

neuro_pt_counts_sum_adult <- neuro_pt_counts %>% 
  filter(population == "Adult") %>% 
  select(-population)

neuro_pt_counts_sum_pediatric <- neuro_pt_counts %>% 
  filter(population == "Pediatric") %>% 
  select(-population)

none_n_adult = neuro_pt_counts_sum_adult$None_n
cns_n_adult = neuro_pt_counts_sum_adult$CNS_n
pns_n_adult = neuro_pt_counts_sum_adult$PNS_n

none_n_pediatric = neuro_pt_counts_sum_pediatric$None_n
cns_n_pediatric = neuro_pt_counts_sum_pediatric$CNS_n
pns_n_pediatric = neuro_pt_counts_sum_pediatric$PNS_n

Calculate Relative Risk (RR)

calculate_rr <- function(is_pediatric = FALSE) {
  
  if(is_pediatric == FALSE) {
    cns_n = cns_n_adult
    pns_n = pns_n_adult
    neuro_pt_counts_sum = neuro_pt_counts_sum_adult
    pop = "Adult"
    # create a list of comorbidities
    list_of_comorbs <- comorb_table_wide %>% 
      filter(population == paste(pop)) %>% 
      distinct(Comorbidity) 
    list_of_comorbs <- list_of_comorbs$Comorbidity
    } else  {
    cns_n = cns_n_pediatric
    pns_n = pns_n_pediatric
    neuro_pt_counts_sum = neuro_pt_counts_sum_pediatric
    pop = "Pediatric"
    # create a list of comorbidities
    list_of_comorbs <- comorb_table_wide %>% 
      filter(population == paste(pop)) %>% 
      distinct(Comorbidity) %>% 
      as.vector()
    list_of_comorbs <- list_of_comorbs$Comorbidity
  }
  
  
  # create empty list to save relative risk calculations
  rr_calcs <- list()
  
  # for each comorbidity, we will calculate the relative risk of having a CNS and PNS diagnosis
  for (i in list_of_comorbs) {
  
    # https://www.cdc.gov/csels/dsepd/ss1978/lesson3/section5.html
    # create matrix as:
    # exposed group - with outcome, without outcome
    # non exposed group - with outcome, without outcome
  
    # where variables ending with `_Total` are the total number of CNS, PNS, or NNC patients with the respective comorbidity.
    # variables ending with `_n` are the total numbers in the entire population
  
    # to calculate confidence intervals
  #https://sphweb.bumc.bu.edu/otlt/mph-modules/bs/bs704_confidence_intervals/bs704_confidence_intervals8.html
  
    rr_cns <- comorb_table_wide %>%
      filter(population == paste(pop),
             Comorbidity == paste(i)) %>%
      mutate(a = CNS_Total, # number CNS w/comorb
             b = Comorb_Total - CNS_Total, # number of comorb pts w/o CNS
             c = cns_n - CNS_Total, # number of CNS pts w/o comorb
             d = sum(neuro_pt_counts_sum) - Comorb_Total - c, # number of pts w/o comorb or CNS
             risk_cns = a/(a+b),
             risk_non_cns = c/(c+d),
             rr_CNS = risk_cns/risk_non_cns,
             # for confidence intervals
             ci_1 = (b/a)/(a+b), # (n1-x1)/n1 
             ci_2 = (d/c)/(c+d), # (n2-x2)/n2
             ci_root = sqrt(ci_1 + ci_2),
             ci_lower = log(rr_CNS) - (1.96*ci_root),
             ci_upper = log(rr_CNS) + (1.96*ci_root),
             l_ci_cns = exp(ci_lower),
             u_ci_cns = exp(ci_upper)) %>%
      select(Comorbidity, Comorb_Total, None_Total, CNS_Total, PNS_Total, rr_CNS, l_ci_cns, u_ci_cns)
  
      rr_pns <- comorb_table_wide %>%
      filter(population == paste(pop),
             Comorbidity == paste(i)) %>%
      mutate(a = PNS_Total, # number PNS w/comorb
             b = Comorb_Total - PNS_Total, # number of comorb pts w/o PNS
             c = pns_n - PNS_Total, # number of PNS pts w/o comorb
             d = sum(neuro_pt_counts_sum) - Comorb_Total - c, # number of pts w/o comorb or PNS
             risk_pns = a/(a+b),
             risk_non_pns = c/(c+d),
             rr_PNS = risk_pns/risk_non_pns,
             # for confidence intervals
             ci_1 = (b/a)/(a+b),
             ci_2 = (d/c)/(c+d),
             ci_root = sqrt(ci_1 + ci_2),
             ci_lower = log(rr_PNS) - (1.96*ci_root),
             ci_upper = log(rr_PNS) + (1.96*ci_root),
             l_ci_pns = exp(ci_lower),
             u_ci_pns = exp(ci_upper)) %>%
      select(Comorbidity, rr_PNS, l_ci_pns, u_ci_pns)
  
      rr <- rr_cns %>%
        left_join(., rr_pns, by = c("Comorbidity"))
  
    rr_calcs[[i]] <- rr
  
  }
  
  rr_results <- bind_rows(rr_calcs) %>%
      mutate(rr_CNS = round(rr_CNS, 2),
           l_ci_cns = round(l_ci_cns, 2),
           u_ci_cns = round(u_ci_cns, 2),
           rr_PNS = round(rr_PNS, 2),
           l_ci_pns = round(l_ci_pns, 2),
           u_ci_pns = round(u_ci_pns, 2),
           `RR CNS (95% CI)` = paste(rr_CNS, "(", l_ci_cns, ",", u_ci_cns, ")"),
           `RR PNS (95% CI)` = paste(rr_PNS, "(", l_ci_pns, ",", u_ci_pns, ")")) %>%
    select(Comorbidity, rr_CNS, l_ci_cns, u_ci_cns, `RR CNS (95% CI)`,
           rr_PNS, l_ci_pns, u_ci_pns, `RR PNS (95% CI)`)
  
  # tidy up data
  write.csv(rr_results %>%
              arrange(desc(rr_CNS)) %>%
              select(Comorbidity, `RR CNS (95% CI)`, `RR PNS (95% CI)`), 
            paste0("tables/Supp.Table4_RR_", pop, ".csv"), row.names = FALSE)
  
    # reformat data
  rr_results_tidy_rr <- rr_results %>%
    select(Comorbidity, rr_CNS, rr_PNS) %>%
    pivot_longer(cols = rr_CNS:rr_PNS, names_to = "Neuro Status", values_to = "RR") %>%
    mutate(`Neuro Status` = if_else(`Neuro Status` == "rr_CNS", "CNS", "PNS"))
  
  rr_results_tidy_l_ci <- rr_results %>%
    select(Comorbidity, l_ci_cns, l_ci_pns) %>%
    pivot_longer(cols = l_ci_cns:l_ci_pns, names_to = "Neuro Status", values_to = "l_CI") %>%
    mutate(`Neuro Status` = if_else(`Neuro Status` == "l_ci_cns", "CNS", "PNS"))
  
  rr_results_tidy_u_ci <- rr_results %>%
    select(Comorbidity, u_ci_cns, u_ci_pns) %>%
    pivot_longer(cols = u_ci_cns:u_ci_pns, names_to = "Neuro Status", values_to = "u_CI") %>%
    mutate(`Neuro Status` = if_else(`Neuro Status` == "u_ci_cns", "CNS", "PNS"))
  
  # combine all tidy results
  rr_results_tidy <- rr_results_tidy_rr %>%
    left_join(., rr_results_tidy_l_ci,
                               by = c("Comorbidity", "Neuro Status")) %>%
    left_join(., rr_results_tidy_u_ci,
                               by = c("Comorbidity", "Neuro Status"))
  
  # order by CNS diagnoses with higher RR
  cns_top_results <- rr_results_tidy %>%
    filter(`Neuro Status` == "CNS") %>%
    arrange(RR) %>%
    select(Comorbidity)
  
  rr_results_tidy$Comorbidity <-  ordered(rr_results_tidy$Comorbidity, levels = cns_top_results$Comorbidity)
  
  cns_rr_plot <- ggplot(rr_results_tidy %>%
           filter(`Neuro Status` == "CNS"),
         aes(x = RR, y = Comorbidity)) +
    geom_vline(aes(xintercept = 1), size = .25, linetype = "dashed") +
      geom_errorbarh(aes(xmax = u_CI, xmin = l_CI), size = 0.7, height = 0.2, color = "gray50") +
      geom_point(size = 1.5, color = "tomato") +
      scale_x_continuous(breaks = seq(0, 4, 1), labels = seq(0, 4, 1),
                         limits = c(0,4)) +
    ylab("") +
    xlab("Relative Risk") +
    ggtitle("CNS Patients") + 
    theme(axis.text.y = element_text(face = "bold"))
  
  # arrange by top PNS results
  pns_top_results <- rr_results_tidy %>%
    filter(`Neuro Status` == "PNS") %>%
    arrange(RR) %>%
    select(Comorbidity)
  
  rr_results_tidy$Comorbidity <-  ordered(rr_results_tidy$Comorbidity, levels = pns_top_results$Comorbidity)
  
  
  pns_rr_plot <- ggplot(rr_results_tidy %>%
           filter(`Neuro Status` == "PNS"),
         aes(x = RR, y = Comorbidity)) +
    geom_vline(aes(xintercept = 1), size = .25, linetype = "dashed") +
      geom_errorbarh(aes(xmax = u_CI, xmin = l_CI), size = 0.70, height = 0.2, color = "gray50") +
      geom_point(size = 1.5, color = "slateblue") +
      scale_x_continuous(breaks = seq(0, 1.5, 0.5), labels = seq(0, 1.5, 0.5),
                         limits = c(0,1.5)) +
    ylab("") +
    xlab("Relative Risk") +
    ggtitle("PNS Patients") + 
    theme(axis.text.y = element_text(face = "bold"))
  
  pns_rr_plot <- set_panel_size(pns_rr_plot,
                            width  = unit(7, "cm"),
                            height = unit(6.5, "in"))
  
  cns_rr_plot <- set_panel_size(cns_rr_plot,
                            width  = unit(7, "cm"),
                            height = unit(6.5, "in"))
  
  #grid.arrange(cns_rr_plot, pns_rr_plot, ncol=2)
  
  ggsave(paste0("figures/Figure4.relative_risk_", pop, ".png"), grid.arrange(cns_rr_plot, pns_rr_plot, ncol=2), width = 15, height = 8, dpi = 300)
  
  }

LPCA deviance explained

How much deviance is explained with 10 principal components for 30 comorbidity types?

Across all healthcare systems, 10 principal components explained above 75% of the deviance.

compare_deviance <- function(sorted_sites, is_pediatric=FALSE) {
  
  pca_dev <- list()
  
  if(is_pediatric==FALSE) {
    population = "adults"
    sorted_sites = sorted_sites[!sorted_sites%in% c("GOSH_results", "BCH_results")]
    } else {
    population = "pediatrics"
    }

  # for each healthcare system, we will calculate the total number of patients with each ICD code
  for (i in sorted_sites) {
    print(i)
    tmp <- get(i)
    tmp_pca <- tmp[[c(
      "comorbidities",
      paste0("comorb_", population),
      "deviance_expl"
    )]] %>% 
      data.frame() %>% 
      rename(dev_expl = '.')
    tmp_pca$site <- tmp[["site"]]
    pca_dev[[i]] <- tmp_pca
    
  }
  
  pca_df <- bind_rows(pca_dev)
  
  return(pca_df)
  
}

pca_adult <- compare_deviance(sorted_sites = adult_sites, is_pediatric = FALSE)
## [1] "APHP_results"
## [1] "FRBDX_results"
## [1] "UKFR_results"
## [1] "ICSM_results"
## [1] "HPG23_results"
## [1] "NUH_results"
## [1] "MGB_results"
## [1] "UPENN_results"
## [1] "UMICH_results"
## [1] "NWU_results"
## [1] "UPITT_results"
## [1] "H12O_results"
## [1] "UKY_results"
## [1] "VA1_results"
## [1] "VA2_results"
## [1] "VA3_results"
## [1] "VA4_results"
## [1] "VA5_results"
## [1] "UCLA_results"
pca_data <- bind_rows(pca_adult)


theme_set(theme_classic())

pca_plot <- ggplot(pca_data, 
       aes(x = fct_reorder(site, -dev_expl), y = dev_expl, group = 1)) +
  geom_point(size = 2.5) +
  geom_line() +
  xlab("Healthcare System") + 
  ylab("Proportion of Deviance explained") +  
  ylim(0,1) + 
  theme(axis.text.x=element_text(angle=90, hjust=1, face = "bold", size = 12),
        axis.text.y=element_text(face = "bold"), 
        axis.title.y = element_text(face = "bold", size = 15)); pca_plot

ggsave("figures/SuppFig_pca_deviance.png", pca_plot, height = 6, width = 6)
LS0tDQp0aXRsZTogIioqQ09WSUQtMTkgYW5kIE5ldXJvbG9naWNhbCBJbGxuZXNzIC0gUGF0aWVudCBDaGFyYWN0ZXJpc3RpY3MqKiINCmF1dGhvcjogIk1lZyBIdXRjaCwgSmkgU29uLCBUcmFuZyBMZSwgQ2h1YW4gSG9uZywgWHVhbiBXYW5nLCBaYWhyYSBTaGFrZXJpIg0KZGF0ZTogIjA0LzExLzIwMjMiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB0cnVlDQogICAgdG9jX2Zsb2F0OiB0cnVlDQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQ0KICAgIHRoZW1lOiBzcGFjZWxhYg0KLS0tDQoNClRoaXMgbm90ZWJvb2sgZGVzY3JpYmVzIHRoZSAqKjEwNiwyMjkqKiBob3NwaXRhbGl6ZWQgQ09WSUQtMTkgcG9zaXRpdmUgcGF0aWVudHMgdGhhdCB3ZXJlIHN0dWRpZWQgYXMgcGFydCBvZiB0aGUgW0NvbnNvcnRpdW0gZm9yIENsaW5pY2FsIENoYXJhY3Rlcml6YXRpb24gb2YgQ09WSUQtMTkgYnkgRUhSXShodHRwczovL2NvdmlkY2xpbmljYWwubmV0LykgTmV1cm9sb2d5IGdyb3VwLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGdyaWRHcmFwaGljcykNCmxpYnJhcnkoZ3JpZEV4dHJhKQ0KbGlicmFyeShnZ3B1YnIpDQpsaWJyYXJ5KERUKQ0KbGlicmFyeShrYWJsZUV4dHJhKQ0KbGlicmFyeShjb3dwbG90KQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KGdsdWUpDQpsaWJyYXJ5KGVnZykNCnNvdXJjZSgiUi9wbG90X3RoZW1lLlIiKQ0Kc291cmNlKCJSL2RlbW9fdGFibGVzLlIiKQ0Kc291cmNlKCJSL3V0aWxzLlIiKQ0KYGBgDQoNCiMgKipJbXBvcnQgRGF0YSBmcm9tIGVhY2ggSGVhbHRoY2FyZSBzeXN0ZW0qKg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyByZWFkIGluIGZpbGVzIGZyb20gcmVzdWx0cyBmb2xkZXIgDQojIHRoaXMgZm9sZGVyIGNvbnRhaW5zIGFsbCBvZiB0aGUgbG9jYWwgaGVhbHRoY2FyZSBzeXN0ZW0gbGV2ZWwgYW5hbHlzZXMNCnJkYXMgPC0gbGlzdC5maWxlcygNCiAgcGF0aCA9ICJyZXN1bHRzIiwNCiAgcGF0dGVybiA9ICIucmRhIiwNCiAgZnVsbC5uYW1lcyA9IFRSVUUNCikNCg0KDQpmb3IgKHJkYSBpbiByZGFzKSB7DQogIGxvYWQocmRhKQ0KfQ0KDQpybShyZGFzLCByZGEpDQoNCiMgY3JlYXRlIGEgbGlzdCBvZiBwYXJ0aWNpcGF0aW5nIGhlYWx0aGNhcmUgc3lzdGVtcyBmcm9tIG91ciBzdHVkeSB0cmFja2luZyBzcHJlYWRzaGVldA0Kc2l0ZV9nb29nbGVfdXJsIDwtICJodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9zcHJlYWRzaGVldHMvZC8xZXBjWU5kXzBqQ1VNa3RPSGY4bXo1djY1MXp5MUpBTEQ2UGd6b2JyR1dEWS9lZGl0P3VzcD1zaGFyaW5nIg0KDQojIGxvYWQgc2l0ZSBwYXJhbWV0ZXJzDQpzaXRlX3BhcmFtcyA8LSBnb29nbGVzaGVldHM0OjpyZWFkX3NoZWV0KHNpdGVfZ29vZ2xlX3VybCwgc2hlZXQgPSAxKQ0Kc2l0ZV9hdmFpbHMgPC0gZ29vZ2xlc2hlZXRzNDo6cmVhZF9zaGVldChzaXRlX2dvb2dsZV91cmwsIHNoZWV0ID0gMikNCg0KIyBmaWx0ZXIgdGhlIGxpc3Qgb2Ygc2l0ZXMgd2hvIHJhbiB0aGUgYW5hbHlzaXMNCnNvcnRlZF9zaXRlcyA8LSBzaXRlX2F2YWlscyAlPiUNCiAgZmlsdGVyKCFpcy5uYShkYXRlX3Y0X3JlY2VpdmVkKSkgJT4lDQogIHB1bGwoc2l0ZWlkKSAlPiUNCiAgcGFzdGUoInJlc3VsdHMiLCBzZXAgPSAiXyIpDQoNCiMgbGlzdCBzaXRlcyB3aXRob3V0IHJhY2UNCnNpdGVzX3dvX3JhY2UgPC0gc2l0ZV9wYXJhbXMgJT4lDQogIGZpbHRlcighaW5jbHVkZV9yYWNlKSAlPiUNCiAgcHVsbChzaXRlaWQpDQoNCiMgY29tYmluZSBhbGwgcmRhIGZpbGVzIHdpdGggJ3Jlc3VsdHMnIGluIG5hbWUNCnJlc3VsdHMgPC0gbWdldChscyhwYXR0ZXJuID0gInJlc3VsdHMiKSkNCg0KIyMgbG9hZCBpbiB0aGUgcHJlLXByb2Nlc3NlZCBjb21vcmJpZGl0eSBhbmQgbWV0YS1kYXRhDQpsb2FkKCJwcm9jZXNzZWQvY29tb3JiaWRpdHlfdGFibGVfY25wcy5yZGEiKQ0KbG9hZCgicHJvY2Vzc2VkL25ldXJvX3B0X2NvdW50cy5yZGEiKSANCmBgYA0KDQojICoqUHJlLXByb2Nlc3NpbmcqKg0KDQoqKjEuIEZvcm1hdCBwcmltYXJ5IGRlbW9ncmFwaGljIGNoYXJhY3RlcmlzdGljcyoqDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIGNyZWF0ZSBsaXN0IG9mIGhvc3BpdGFscyBmb3IgYWR1bHQgYW5kIHBlZGlhdHJpYyBhbmFseXNlcw0KYWR1bHRfc2l0ZXMgPSBzb3J0ZWRfc2l0ZXNbIXNvcnRlZF9zaXRlcyAlaW4lIGMoIkJDSF9yZXN1bHRzIiwgIkdPU0hfcmVzdWx0cyIpXQ0KDQpwZWRpYXRyaWNfc2l0ZXMgPSBzb3J0ZWRfc2l0ZXNbIXNvcnRlZF9zaXRlcyAlaW4lIGMoIlZBMV9yZXN1bHRzIiwgIlZBMl9yZXN1bHRzIiwgIlZBM19yZXN1bHRzIiwgIlZBNF9yZXN1bHRzIiwgIlZBNV9yZXN1bHRzIildDQoNCmRlbW9fdGFibGVfYWR1bHQgPC0gY3JlYXRlX2RlbW9fdGFibGVvbmUoc29ydGVkX3NpdGVzID0gYWR1bHRfc2l0ZXMsIGlzX3BlZGlhdHJpYyA9IEZBTFNFKQ0KDQpkZW1vX3RhYmxlX3BlZGlhdHJpYyA8LSBjcmVhdGVfZGVtb190YWJsZW9uZShzb3J0ZWRfc2l0ZXMgPSBwZWRpYXRyaWNfc2l0ZXMsIGlzX3BlZGlhdHJpYyA9IFRSVUUpDQoNCmRlbW9fdGFibGVfY29tYmluZSA8LSByYmluZChkZW1vX3RhYmxlX2FkdWx0LCBkZW1vX3RhYmxlX3BlZGlhdHJpYykNCmBgYA0KDQoqKjIuIEZvcm1hdCBjbGluaWNhbCBjaGFyYWN0ZXJpc3RpY3MgKGkuZTogY29udGludW91cyB2YXJpYWJsZXMgc3VjaCBhcyBtZWRpYW4gdGltZSB0byBkaXNjaGFyZ2UpKioNCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KY2xpbmljYWxfdGFibGVfYWR1bHQgPC0gY3JlYXRlX2NsaW5pY2FsX3RhYmxlb25lKHNvcnRlZF9zaXRlcyA9IGFkdWx0X3NpdGVzLCBpc19wZWRpYXRyaWMgPSBGQUxTRSkNCg0KY2xpbmljYWxfdGFibGVfcGVkaWF0cmljIDwtIGNyZWF0ZV9jbGluaWNhbF90YWJsZW9uZShzb3J0ZWRfc2l0ZXMgPSBwZWRpYXRyaWNfc2l0ZXMsIGlzX3BlZGlhdHJpYyA9IFRSVUUpDQoNCmNsaW5pY2FsX3RhYmxlX2NvbWJpbmUgPC0gcmJpbmQoY2xpbmljYWxfdGFibGVfYWR1bHQsIGNsaW5pY2FsX3RhYmxlX3BlZGlhdHJpYykNCmBgYA0KDQoNCioqMy4gQ29uZHVjdCBhZGRpdGlvbmFsIHByZS1wcm9jZXNzaW5nIG9mIGNsaW5pY2FsIHZhcmlhYmxlcyoqDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpjbGluaWNhbF90YWJsZV9hZHVsdF9jbGVhbiA8LSBjbGVhbl9jbGluaWNhbF90YWJsZXMoY2xpbmljYWxfdGFibGUgPSBjbGluaWNhbF90YWJsZV9hZHVsdCkNCg0KY2xpbmljYWxfdGFibGVfcGVkaWF0cmljX2NsZWFuIDwtIGNsZWFuX2NsaW5pY2FsX3RhYmxlcyhjbGluaWNhbF90YWJsZSA9IGNsaW5pY2FsX3RhYmxlX3BlZGlhdHJpYykNCg0KY2xpbmljYWxfdGFibGVfY2xlYW5fY29tYmluZSA8LSByYmluZChjbGluaWNhbF90YWJsZV9hZHVsdF9jbGVhbiwgY2xpbmljYWxfdGFibGVfcGVkaWF0cmljX2NsZWFuKQ0KYGBgDQoNCioqQWRkIENvbW9yYmlkaXR5IGRhdGEqKg0KDQpgYGB7cn0NCmNvbW9yYmlkaXR5X3RhYmxlX2FkdWx0cyA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUNCiAgZmlsdGVyKHBvcHVsYXRpb24gPT0gIkFkdWx0IikgJT4lIA0KICBhcnJhbmdlKGRlc2MoQ29tb3JiX1RvdGFsKSkgJT4lIA0KICBzbGljZSgxOjQpICMgcmV0dXJuIHRvcCBjb21vcmJpZGl0aWVzDQoNCmNvbW9yYmlkaXR5X3RhYmxlX3BlZGlhdHJpYyA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUNCiAgZmlsdGVyKHBvcHVsYXRpb24gPT0gIlBlZGlhdHJpYyIpICU+JSANCiAgYXJyYW5nZShkZXNjKENvbW9yYl9Ub3RhbCkpICU+JSANCiAgc2xpY2UoMTo0KSAjIHJldHVybiB0b3AgY29tb3JiaWRpdGllcw0KDQp0b3BfY29tb3JiX2FkdWx0cyA8LSBjb21vcmJpZGl0eV90YWJsZV9hZHVsdHMkQ29tb3JiaWRpdHkNCnRvcF9jb21vcmJfcGVkaWF0cmljcyA8LSBjb21vcmJpZGl0eV90YWJsZV9wZWRpYXRyaWMkQ29tb3JiaWRpdHkNCmBgYA0KDQojICoqRGVtb2dyYXBoaWNzKioNCg0KIyMgVGFibGUgT25lIC0gQ29tYmluZWQNCg0KYGBge3J9DQpuZXVyb19wdF9jb3VudHNfc3VtIDwtIGNvbFN1bXMobmV1cm9fcHRfY291bnRzWywtMV0pICU+JSANCiAgZGF0YS5mcmFtZSgpICU+JSANCiAgdCgpICU+JSANCiAgZGF0YS5mcmFtZSgpDQoNCiMgc3BlY2lmeSB0aGUgb3JkZXIgb2YgVGFibGUgMQ0Kcm93X29yZGVyX2FkdWx0IDwtIGMoDQogICJBbGwgUGF0aWVudHMiLCAiRmVtYWxlIiwgIk1hbGUiLCAiVW5rbm93biBTZXgiLA0KICAiMC0yIiwgIjMtNSIsICI2LTExIiwgIjEyLTE3IiwgIjE4LTI1IiwNCiAgIjI2LTQ5IiwgIjUwLTY5IiwgIjcwLTc5IiwgIjgwKyIsICJVbmtub3duIEFnZSIsDQogICJBbWVyaWNhbiBJbmRpYW4iLCAiQXNpYW4iLCAiQmxhY2siLA0KICAiSGF3YWlpYW4vUGFjaWZpYyBJc2xhbmRlciIsDQogICJIaXNwYW5pYy9MYXRpbm8iLCAiV2hpdGUiLCAiT3RoZXIiLA0KICB0b3BfY29tb3JiX2FkdWx0cywgDQogICJNZWRpYW4gRWxpeGhhdXNlciBzY29yZSAgKHNkKSAiLA0KICAiTWVkaWFuIHByZSBhZG1pc3Npb24gY25zICAoc2QpICIsDQogICJNZWRpYW4gcHJlIGFkbWlzc2lvbiBwbnMgIChzZCkgIiwNCiAgIk5vbi1TZXZlcmUiLCAiU2V2ZXJlIiwNCiAgIk1lZGlhbiB0aW1lIHRvIHNldmVyZSAgKHNkKSAiLA0KICAiQWxpdmUiLCAiRGVjZWFzZWQiLA0KICAiTWVkaWFuIHRpbWUgdG8gZGVhdGggIChzZCkgIiwNCiAgIkRpc2NoYXJnZWQiLCAiTm90IERpc2NoYXJnZWQiLA0KICAiTWVkaWFuIHRpbWUgdG8gZmlyc3QgZGlzY2hhcmdlICAoc2QpICIsDQogICJOb3QgUmVhZG1pdHRlZCIsICJSZWFkbWl0dGVkIiwNCiAgIk1lZGlhbiB0aW1lIHRvIGZpcnN0IHJlYWRtaXNzaW9uICAoc2QpICIsDQogICJNZWRpYW4gbnVtYmVyIG9mIHJlYWRtaXNzaW9ucyAgKHNkKSAiDQogICkNCg0KDQp0YWJsZW9uZV9jb21iaW5lIDwtIGNyZWF0ZV90YWJsZW9uZShkZW1vX3RhYmxlID0gZGVtb190YWJsZV9jb21iaW5lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdGFibGUgPSBjbGluaWNhbF90YWJsZV9jbGVhbl9jb21iaW5lKSAlPiUNCiAgcmJpbmQoLiwgY29tb3JiX3RhYmxlX3dpZGUgJT4lDQogICAgICAgICAgZ3JvdXBfYnkoQ29tb3JiaWRpdHkpICU+JQ0KICAgICAgICAgIG11dGF0ZShDb21vcmJfVG90YWwgPSBzdW0oQ29tb3JiX1RvdGFsLCBuYS5ybSA9IFRSVUUpLA0KICAgICAgICAgIE5vbmVfc3VtID0gc3VtKE5vbmVfVG90YWwsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgQ05TX3N1bSA9IHN1bShDTlNfVG90YWwsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgUE5TX3N1bSA9IHN1bShQTlNfVG90YWwsIG5hLnJtID0gVFJVRSksDQogICAgICAgICAgTm9uZV9wZXJjID0gcm91bmQoTm9uZV9zdW0vbmV1cm9fcHRfY291bnRzX3N1bSROb25lX24qMTAwLDEpLA0KICAgICAgICAgIENlbnRyYWxfcGVyYyA9IHJvdW5kKENOU19zdW0vbmV1cm9fcHRfY291bnRzX3N1bSRDTlNfbioxMDAsMSksDQogICAgICAgICAgUGVyaXBoZXJhbF9wZXJjID0gcm91bmQoUE5TX3N1bS9uZXVyb19wdF9jb3VudHNfc3VtJFBOU19uKjEwMCwxKSwNCiAgICAgICAgICBOb25lID0gcGFzdGUoTm9uZV9zdW0sICIoIiwgTm9uZV9wZXJjLCAiKSIpLA0KICAgICAgICAgIENlbnRyYWwgPSBwYXN0ZShDTlNfc3VtLCAiKCIsIENlbnRyYWxfcGVyYywgIikiKSwNCiAgICAgICAgICBQZXJpcGhlcmFsID0gcGFzdGUoUE5TX3N1bSwgIigiLCBQZXJpcGhlcmFsX3BlcmMsICIpIikpICU+JQ0KICAgICAgICAgIGRpc3RpbmN0KENvbW9yYmlkaXR5LCBDb21vcmJfVG90YWwsIE5vbmUsIENlbnRyYWwsIFBlcmlwaGVyYWwpICU+JQ0KICAgICAgICAgIHJlbmFtZShgVGFibGUgMWAgPSAiQ29tb3JiaWRpdHkiLA0KICAgICAgICAgIE4gPSAiQ29tb3JiX1RvdGFsIikpICU+JQ0KICBzbGljZShtYXRjaChyb3dfb3JkZXJfYWR1bHQsIGBUYWJsZSAxYCkpDQoNCmtibCh0YWJsZW9uZV9jb21iaW5lICU+JQ0KICAgICAgcmVuYW1lKCJObyBOZXVyb2xvZ2ljYWwgQ29uZGl0aW9uIChOTkMpIiA9IE5vbmUpKSAlPiUNCiAgICAgIGFkZF9oZWFkZXJfYWJvdmUoYygNCiAgICAgICIgIiwNCiAgICAgICJUb3RhbCBQYXRpZW50cyIgPSAxLA0KICAgICAgIk5ldXJvbG9naWNhbCBEaXNlYXNlIiA9IDMNCiAgICAgICkpICU+JQ0KICAgICAga2FibGVfcGFwZXIoInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRikgJT4lDQogICAgICBwYWNrX3Jvd3MoIlNleCIsIDIsIDQpICU+JQ0KICAgICAgcGFja19yb3dzKCJBZ2UiLCA1LCAxMykgJT4lDQogICAgICBwYWNrX3Jvd3MoIlJhY2UgJiBFdGhuaWNpdHkiLCAxNCwgMjApICU+JQ0KICAgICAgcGFja19yb3dzKCJQYXN0IE1lZGljYWwgSGlzdG9yeSIsIDIxLCAyNykgJT4lDQogICAgICBwYWNrX3Jvd3MoIlNldmVyaXR5IiwgMjgsIDMwKSAlPiUNCiAgICAgIHBhY2tfcm93cygiU3Vydml2YWwiLCAzMSwgMzMpICU+JQ0KICAgICAgcGFja19yb3dzKCJEaXNjaGFyZ2UiLCAzNCwgMzYpICU+JQ0KICAgICAgcGFja19yb3dzKCJSZWFkbWlzc2lvbiIsIDM3LCA0MCkNCndyaXRlLmNzdih0YWJsZW9uZV9jb21iaW5lLCAndGFibGVzL3RhYmxlMS5jc3YnLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQojIyBUYWJsZSBPbmUgLSBBZHVsdHMNCg0KYGBge3J9DQp0YWJsZW9uZV9hZHVsdCA8LSBjcmVhdGVfdGFibGVvbmUoZGVtb190YWJsZSA9IGRlbW9fdGFibGVfYWR1bHQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xpbmljYWxfdGFibGUgPSBjbGluaWNhbF90YWJsZV9hZHVsdF9jbGVhbikgJT4lDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmJpbmQoLiwgY29tb3JiaWRpdHlfdGFibGVfYWR1bHRzICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KENvbW9yYmlkaXR5LCBDb21vcmJfVG90YWwsIGBOTkNfTl8lYCwgYENOU19OXyVgLCBgUE5TX05fJWApICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVuYW1lKGBUYWJsZSAxYCA9ICJDb21vcmJpZGl0eSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTiA9ICJDb21vcmJfVG90YWwiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5vbmUgPSAiTk5DX05fJSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ2VudHJhbCA9ICJDTlNfTl8lIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQZXJpcGhlcmFsID0gIlBOU19OXyUiKSkgJT4lIA0KICBzbGljZShtYXRjaChyb3dfb3JkZXJfYWR1bHQsIGBUYWJsZSAxYCkpDQoNCmtibCh0YWJsZW9uZV9hZHVsdCAlPiUNCiAgICByZW5hbWUoIk5vIE5ldXJvbG9naWNhbCBDb25kaXRpb24gKE5OQykiID0gTm9uZSkpICU+JQ0KICBhZGRfaGVhZGVyX2Fib3ZlKGMoDQogICIgIiwNCiAgIlRvdGFsIFBhdGllbnRzIiA9IDEsDQogICJOZXVyb2xvZ2ljYWwgRGlzZWFzZSIgPSAzDQogICkpICU+JQ0KICBrYWJsZV9wYXBlcigic3RyaXBlZCIsIGZ1bGxfd2lkdGggPSBGKSAlPiUNCiAgcGFja19yb3dzKCJTZXgiLCAyLCA0KSAlPiUNCiAgcGFja19yb3dzKCJBZ2UiLCA1LCA5KSAlPiUNCiAgcGFja19yb3dzKCJSYWNlICYgRXRobmljaXR5IiwgMTAsIDE2KSAlPiUNCiAgcGFja19yb3dzKCJQYXN0IE1lZGljYWwgSGlzdG9yeSIsIDE3LCAyMykgJT4lDQogIHBhY2tfcm93cygiU2V2ZXJpdHkiLCAyNCwgMjYpICU+JQ0KICBwYWNrX3Jvd3MoIlN1cnZpdmFsIiwgMjcsIDI5KSAlPiUNCiAgcGFja19yb3dzKCJEaXNjaGFyZ2UiLCAzMCwgMzIpICU+JQ0KICBwYWNrX3Jvd3MoIlJlYWRtaXNzaW9uIiwgMzMsIDM2KQ0KYGBgDQoNCiMjIFRhYmxlIE9uZSAtIFBlZGlhdHJpY3MNCg0KYGBge3J9DQojIHNwZWNpZnkgdGhlIG9yZGVyIG9mIFRhYmxlIDENCnJvd19vcmRlcl9wZWRpYXRyaWMgPC0gYygNCiAgIkFsbCBQYXRpZW50cyIsICJGZW1hbGUiLCAiTWFsZSIsICJVbmtub3duIFNleCIsDQogICIwLTIiLCAiMy01IiwgIjYtMTEiLCAiMTItMTciLCAiMTgtMjUiLA0KICAiMjYtNDkiLCAiNTAtNjkiLCAiNzAtNzkiLCAiODArIiwgIlVua25vd24gQWdlIiwNCiAgIkFtZXJpY2FuIEluZGlhbiIsICJBc2lhbiIsICJCbGFjayIsDQogICJIYXdhaWlhbi9QYWNpZmljIElzbGFuZGVyIiwNCiAgIkhpc3BhbmljL0xhdGlubyIsICJXaGl0ZSIsICJPdGhlciIsDQogIHRvcF9jb21vcmJfcGVkaWF0cmljcywgDQogICJNZWRpYW4gRWxpeGhhdXNlciBzY29yZSAgKHNkKSAiLA0KICAiTWVkaWFuIHByZSBhZG1pc3Npb24gY25zICAoc2QpICIsDQogICJNZWRpYW4gcHJlIGFkbWlzc2lvbiBwbnMgIChzZCkgIiwNCiAgIk5vbi1TZXZlcmUiLCAiU2V2ZXJlIiwNCiAgIk1lZGlhbiB0aW1lIHRvIHNldmVyZSAgKHNkKSAiLA0KICAiQWxpdmUiLCAiRGVjZWFzZWQiLA0KICAiTWVkaWFuIHRpbWUgdG8gZGVhdGggIChzZCkgIiwNCiAgIkRpc2NoYXJnZWQiLCAiTm90IERpc2NoYXJnZWQiLA0KICAiTWVkaWFuIHRpbWUgdG8gZmlyc3QgZGlzY2hhcmdlICAoc2QpICIsDQogICJOb3QgUmVhZG1pdHRlZCIsICJSZWFkbWl0dGVkIiwNCiAgIk1lZGlhbiB0aW1lIHRvIGZpcnN0IHJlYWRtaXNzaW9uICAoc2QpICIsDQogICJNZWRpYW4gbnVtYmVyIG9mIHJlYWRtaXNzaW9ucyAgKHNkKSAiDQogICkNCg0KdGFibGVvbmVfcGVkaWF0cmljIDwtIGNyZWF0ZV90YWJsZW9uZShkZW1vX3RhYmxlID0gZGVtb190YWJsZV9wZWRpYXRyaWMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsaW5pY2FsX3RhYmxlID0gY2xpbmljYWxfdGFibGVfcGVkaWF0cmljX2NsZWFuKSAgJT4lDQogIHJiaW5kKC4sIGNvbW9yYmlkaXR5X3RhYmxlX3BlZGlhdHJpYyAlPiUNCiAgICAgICAgICBzZWxlY3QoQ29tb3JiaWRpdHksIENvbW9yYl9Ub3RhbCwgYE5OQ19OXyVgLCBgQ05TX05fJWAsIGBQTlNfTl8lYCkgJT4lIA0KICAgICAgICAgIHJlbmFtZShgVGFibGUgMWAgPSAiQ29tb3JiaWRpdHkiLA0KICAgICAgICAgICAgICAgICAgTiA9ICJDb21vcmJfVG90YWwiLA0KICAgICAgICAgICAgICAgICAgTm9uZSA9ICJOTkNfTl8lIiwNCiAgICAgICAgICAgICAgICAgIENlbnRyYWwgPSAiQ05TX05fJSIsDQogICAgICAgICAgICAgICAgICBQZXJpcGhlcmFsID0gIlBOU19OXyUiKSkgJT4lIA0KICBzbGljZShtYXRjaChyb3dfb3JkZXJfcGVkaWF0cmljLCBgVGFibGUgMWApKQ0KDQprYmwodGFibGVvbmVfcGVkaWF0cmljICU+JQ0KICAgICAgcmVuYW1lKCJObyBOZXVyb2xvZ2ljYWwgQ29uZGl0aW9uIChOTkMpIiA9IE5vbmUpKSAlPiUNCiAgYWRkX2hlYWRlcl9hYm92ZShjKA0KICAiICIsDQogICJUb3RhbCBQYXRpZW50cyIgPSAxLA0KICAiTmV1cm9sb2dpY2FsIERpc2Vhc2UiID0gMw0KICApKSAlPiUNCiAga2FibGVfcGFwZXIoInN0cmlwZWQiLCBmdWxsX3dpZHRoID0gRikgJT4lDQogIHBhY2tfcm93cygiU2V4IiwgMiwgNCkgJT4lDQogIHBhY2tfcm93cygiQWdlIiwgNSwgOCkgJT4lDQogIHBhY2tfcm93cygiUmFjZSAmIEV0aG5pY2l0eSIsIDksIDE1KSAlPiUNCiAgcGFja19yb3dzKCJQYXN0IE1lZGljYWwgSGlzdG9yeSIsIDE2LCAyMikgJT4lDQogIHBhY2tfcm93cygiU2V2ZXJpdHkiLCAyMywgMjUpICU+JQ0KICBwYWNrX3Jvd3MoIlN1cnZpdmFsIiwgMjYsIDI4KSAlPiUNCiAgcGFja19yb3dzKCJEaXNjaGFyZ2UiLCAyOSwgMzEpICU+JQ0KICBwYWNrX3Jvd3MoIlJlYWRtaXNzaW9uIiwgMzIsIDM1KQ0KYGBgDQoNCioqU2FtcGxlIFNpemUqKg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kbl9hZHVsdCA8LSBhcy5udW1lcmljKHRhYmxlb25lX2FkdWx0JE4pWzFdDQpuX3BlZGlhdHJpYyA8LSBhcy5udW1lcmljKHRhYmxlb25lX3BlZGlhdHJpYyROKVsxXQ0KYGBgDQoNCiMgKipEZW1vZ3JhcGhpYyAmIENsaW5pY2FsIENvdXJzZSBBbmFseXNpcyoqDQoNCkV2YWx1YXRlIGNoYXJhY3RlcmlzdGljcyBhbW9uZyBuZXVybyBzdGF0dXMgZ3JvdXBzDQoNCioqMS4gQ29uZHVjdCBjaGktc3F1YXJlZCB0ZXN0cyBmb3IgY2F0ZWdvcmljYWwgZGVtb2dyYXBoaWMgdmFyaWFibGVzKioNCg0KYGBge3J9DQpjb21wdXRlX2NoaV9zcXVhcmUgPC0gZnVuY3Rpb24odGFibGVvbmUsIGRlbW9fdGFibGUpIHsNCiAgDQogIE4gPSB0YWJsZW9uZSAlPiUgc2VsZWN0KE4pICU+JSBoZWFkKDEpICU+JSBhcy5udW1lcmljKCkNCg0KICAjIHN1bSBvZiBhbGwgbmV1cm8gY29uZGl0aW9uIGdyb3Vwcw0KICBjbnNfbiA9IGFzLm51bWVyaWMoZ3N1YiggIiAuKiQiLCAiIiwgdGFibGVvbmVbMSwiQ2VudHJhbCJdKSkNCiAgcG5zX24gPSBhcy5udW1lcmljKGdzdWIoICIgLiokIiwgIiIsIHRhYmxlb25lWzEsIlBlcmlwaGVyYWwiXSkpDQogIG5vbmVfbiA9IGFzLm51bWVyaWMoZ3N1YiggIiAuKiQiLCAiIiwgdGFibGVvbmVbMSwiTm9uZSJdKSkNCiAgDQogICMgc3VtIHVwIHRoZSB0b3RhbCBjb3VudHMgb2YgZWFjaCBkZW1vZ3JhcGhpYyB2YXJpYWJsZSBhY3Jvc3MgaGVhbHRoY2FyZSBzeXN0ZW1zDQogIHRhYmxlT25lX3N1bXMgPC0gZGVtb190YWJsZSAlPiUNCiAgICBncm91cF9ieSh2YXJpYWJsZSwgRGVtb192YXIsIERlbW9fdmFyX2kpICU+JQ0KICAgIHN1bW1hcmlzZShhY3Jvc3MoDQogICAgICBzdGFydHNfd2l0aCgibl92YXIiKSwNCiAgICAgIGZ1bmN0aW9uKHgpIHN1bSh4LCBuYS5ybSA9IFRSVUUpDQogICAgKSwgLmdyb3VwcyA9ICJkcm9wIikNCiAgDQogICMgY3JlYXRlIGxpc3Qgb2YgZGVtb2dyYXBoaWMvY2xpbmljYWwgdmFyaWFibGVzDQogIHZhcnMgPSB0YWJsZU9uZV9zdW1zJHZhcmlhYmxlDQogIA0KICAjIGZvciBiaW5hcnkgdmFyaWFibGVzLCB3ZSBzaG91bGQgZ3JvdXAgYnkgY2F0ZWdvcnkgDQogICMgVGh1cywgb25seSBydW4gdGhlIGFuYWx5c2lzIGZvciBzZXgubWFsZSByYXRoZXIgdGhhbiBib3RoIHNleC5mZW1hbGUgQU5EIHNleC5tYWxlIHdoaWNoIHdvdWxkIGJlIHJlZHVuZGFudA0KICAjIHdlIHdpbGwgYWxzbyByZW1vdmUgYGFnZV9ncm91cC51bmtub3duYCBzaW5jZSB0aGVpciBpcyBvbmx5IDEgcGF0aWVudCBoZXJlDQogIGV4Y2x1ZGUgPC0gYygicmVhZG1pdHRlZC5mYWxzZSIsICJzZXguZmVtYWxlIiwgInNleC5vdGhlciIsICJzdXJ2aXZhbC5kZWNlYXNlZCIsICJzZXZlcml0eS5ub24tc2V2ZXJlIiwNCiAgICAgICAgICAgICAgICJjb3ZpZF9kaXNjaGFyZ2VkLmRpc2NoYXJnZWQiLCAiYWdlX2dyb3VwLlVua25vd24iKQ0KICB2YXJzIDwtIHZhcnNbIXZhcnMgJWluJSBleGNsdWRlXQ0KICANCiAgIyBjcmVhdGUgZW1wdHkgbGlzdCBmb3IgY2hpLnNxdWFyZSB0ZXN0IHJlc3VsdHMNCiAgY2hpX3Jlc3VsdF9saXN0ID0gbGlzdCgpDQogIA0KICAjIGZvciBlYWNoIHZhcmlhYmxlLCB3ZSB3aWxsIHJ1biB0aGUgY2hpLnNxdWFyZSB0ZXN0DQogIGZvcihpIGluIHZhcnMpIHsNCiAgICB0ZXN0ID0gY2FsY19jaGlzcShpLCB0YWJsZU9uZV9zdW1zLCBub25lX24sIHBuc19uLCBjbnNfbikNCiAgICBYMiA8LSByb3VuZCh0ZXN0JHN0YXRpc3RpYywgNCkNCiAgICBwX3ZhbHVlIDwtIHRlc3QkcC52YWx1ZQ0KICAgIGRmIDwtIHRlc3QkcGFyYW1ldGVyDQogICAgVmFyaWFibGUgPSBwYXN0ZShpKQ0KICAgIA0KICAgIGNoaV9yZXN1bHRzIDwtIGNiaW5kKFZhcmlhYmxlLCBYMiwgcF92YWx1ZSwgZGYpICU+JSANCiAgICAgIGRhdGEuZnJhbWUoKSAlPiUgDQogICAgICBtdXRhdGUocF92YWx1ZSA9IGFzLm51bWVyaWMocF92YWx1ZSkpDQogICAgDQogICAgcm93bmFtZXMoY2hpX3Jlc3VsdHMpIDwtIE5VTEwNCiAgICANCiAgICBjaGlfcmVzdWx0X2xpc3RbW2ldXSA8LSBjaGlfcmVzdWx0cw0KICB9DQogIA0KICAjIHNhdmUgbGlzdCBvZiBjaGkuc3F1YXJlIHRlc3QgcmVzdWx0cw0KICBjaGlzcV9yZXN1bHRzIDwtIGJpbmRfcm93cyhjaGlfcmVzdWx0X2xpc3QpDQogIA0KICByZXR1cm4oY2hpc3FfcmVzdWx0cykNCiAgDQogIA0KICB9DQpgYGANCg0KKipDb21iaW5lZCBBZHVsdCAmIFBlZGlhdHJpYyoqDQoNCmBgYHtyfQ0KY2hpc3FfY29tYmluZSA8LSBjb21wdXRlX2NoaV9zcXVhcmUodGFibGVvbmUgPSB0YWJsZW9uZV9jb21iaW5lICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoIWBUYWJsZSAxYCA9PSAnRGlzY2hhcmdlZCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAhYFRhYmxlIDFgID09ICdOb3QgRGlzY2hhcmdlZCcpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVtb190YWJsZSA9IGRlbW9fdGFibGVfY29tYmluZSAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCF2YXJpYWJsZSA9PSAnY292aWRfZGlzY2hhcmdlZC5kaXNjaGFyZ2VkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICF2YXJpYWJsZSA9PSAnY292aWRfZGlzY2hhcmdlZC5ub3QgZGlzY2hhcmdlZCcpKQ0KDQpkYXRhdGFibGUoY2hpc3FfY29tYmluZSAlPiUgYXJyYW5nZShwX3ZhbHVlKSkNCmBgYA0KDQoqKkFkdWx0KioNCg0KYGBge3J9DQpjaGlzcV9hZHVsdCA8LSBjb21wdXRlX2NoaV9zcXVhcmUodGFibGVvbmUgPSB0YWJsZW9uZV9hZHVsdCAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCFgVGFibGUgMWAgPT0gJ0Rpc2NoYXJnZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWBUYWJsZSAxYCA9PSAnTm90IERpc2NoYXJnZWQnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbW9fdGFibGUgPSBkZW1vX3RhYmxlX2FkdWx0ICU+JQ0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoIXZhcmlhYmxlID09ICdjb3ZpZF9kaXNjaGFyZ2VkLmRpc2NoYXJnZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIXZhcmlhYmxlID09ICdjb3ZpZF9kaXNjaGFyZ2VkLm5vdCBkaXNjaGFyZ2VkJykpDQoNCmRhdGF0YWJsZShjaGlzcV9hZHVsdCAlPiUgYXJyYW5nZShwX3ZhbHVlKSkNCmBgYA0KDQoqKlBlZGlhdHJpYyoqDQoNCmBgYHtyfQ0KY2hpc3FfcGVkaWF0cmljIDwtIGNvbXB1dGVfY2hpX3NxdWFyZSh0YWJsZW9uZSA9IHRhYmxlb25lX3BlZGlhdHJpYyAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCFgVGFibGUgMWAgPT0gJ0Rpc2NoYXJnZWQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWBUYWJsZSAxYCA9PSAnTm90IERpc2NoYXJnZWQnKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbW9fdGFibGUgPSBkZW1vX3RhYmxlX3BlZGlhdHJpYyAlPiUNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyKCF2YXJpYWJsZSA9PSAnY292aWRfZGlzY2hhcmdlZC5kaXNjaGFyZ2VkJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICF2YXJpYWJsZSA9PSAnY292aWRfZGlzY2hhcmdlZC5ub3QgZGlzY2hhcmdlZCcpKQ0KDQpkYXRhdGFibGUoY2hpc3FfcGVkaWF0cmljICU+JSBhcnJhbmdlKHBfdmFsdWUpKQ0KYGBgDQoNCioqMi4gQ29uZHVjdCBhbm92YSAoS3J1c2thbC1XYWxsaXMpIHRlc3RzIGZvciBjb250aW51b3VzIGNsaW5pY2FsIHZhcmlhYmxlcyoqDQoNCmBgYHtyfQ0KY29tcHV0ZV9rcnVza2FsIDwtIGZ1bmN0aW9uKGNsaW5pY2FsX3RhYmxlKSB7DQoNCiAgIyBjcmVhdGUgZW1wdHkgbGlzdCB0byBzYXZlIHByb2Nlc3NlZCB2YXJpYWJsZXMNCiAgcHJvY2Vzc2VkX2xpc3RfZmlncyA8LSBsaXN0KCkNCiAgDQogICMgY3JlYXRlIGEgbGlzdCBvZiBjbGluaWNhbCB2YXJpYWJsZXMgdG8gZnVydGhlciBwcmUtcHJvY2Vzcw0KICB2YXJzX3RvX3Byb2Nlc3MgPC0gdW5pcXVlKGNsaW5pY2FsX3RhYmxlJG5hbWUpDQogIA0KICAjIGZvciBlYWNoIHZhcmlhYmxlLCB3ZSB3aWxsIHJlbW92ZSB0aGUgW21pbiwgbWF4XSBhcyBiZWZvcmUNCiAgZm9yIChpIGluIHZhcnNfdG9fcHJvY2Vzcykgew0KICAgIG1vZF90YWJsZSA8LSBjbGluaWNhbF90YWJsZSAlPiUNCiAgICAgIHJlbmFtZSgidmFyIiA9IG5hbWUpICU+JQ0KICAgICAgZmlsdGVyKHZhciA9PSBpKSAlPiUNCiAgICAgIG11dGF0ZSgNCiAgICAgICAgc2l0ZSA9IHRvdXBwZXIoc2l0ZSksDQogICAgICAgIE5vbmUgPSBzdWIoIihcXCguKnxcXFsuKikiLCAiIiwgTm9uZSksDQogICAgICAgIFBlcmlwaGVyYWwgPSBzdWIoIihcXCguKnxcXFsuKikiLCAiIiwgUGVyaXBoZXJhbCksDQogICAgICAgIENlbnRyYWwgPSBzdWIoIihcXCguKnxcXFsuKikiLCAiIiwgQ2VudHJhbCkNCiAgICAgICkgJT4lDQogICAgICBtdXRhdGUoYWNyb3NzKE5vbmU6Q2VudHJhbCwgYXMubnVtZXJpYykpDQogIA0KICAgIHByb2Nlc3NlZF9saXN0X2ZpZ3NbW2ldXSA8LSBtb2RfdGFibGUNCiAgfQ0KICANCiAgY2xpbmljYWxfdGFibGVfYW5vdmEgPC0gYmluZF9yb3dzKHByb2Nlc3NlZF9saXN0X2ZpZ3MpDQogIA0KICAjIGZvcm1hdCB0aGUgY29udGludW91cyB2YXJpYWJsZXMNCiAgY29udF92YXJzID0gY2xpbmljYWxfdGFibGVfYW5vdmEgJT4lDQogICAgbXV0YXRlKA0KICAgICAgIyBjcmVhdGUgYSB2YXJpYWJsZSB3aXRob3V0IG1lYW4vbWVkaWFuIHByZWZpeA0KICAgICAgZ3JvdXBlZF92YXIgPSBnc3ViKCJNZWFuIHxNZWRpYW4gfCBcXFtNaW4sIE1heFxcXXwgXFwoU0RcXCkiLCAiIiwgdmFyKQ0KICAgICkgJT4lDQogICAgcGl2b3RfbG9uZ2VyKA0KICAgICAgY29scyA9IE5vbmU6Q2VudHJhbCwNCiAgICAgIG5hbWVzX3RvID0gInR5cGUiDQogICAgKSAlPiUNCiAgICBtdXRhdGUodHlwZSA9IGZhY3Rvcih0eXBlLCBsZXZlbHMgPSBjKCJOb25lIiwgIkNlbnRyYWwiLCAiUGVyaXBoZXJhbCIpKSkgJT4lIA0KICAgICMgc2VsZWN0IHZhcmlhYmxlcyB0byBhbmFseXplDQogICAgZmlsdGVyKGdyb3VwZWRfdmFyICVpbiUgYygiRWxpeGhhdXNlciBzY29yZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicHJlIGFkbWlzc2lvbiBjbnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwcmUgYWRtaXNzaW9uIHBucyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRpbWUgdG8gZGVhdGgiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0aW1lIHRvIGZpcnN0IGRpc2NoYXJnZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInRpbWUgdG8gc2V2ZXJlIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJudW1iZXIgb2YgcmVhZG1pc3Npb25zIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJ0aW1lIHRvIGZpcnN0IHJlYWRtaXNzaW9uIikpICU+JSANCiAgICBkaXN0aW5jdCgpDQogIA0KICAjIGNyZWF0ZSBmdW5jdGlvbiB0byBjb25kdWN0IGFub3ZhDQogIGNvbnRfdmFyX3Jlc3VsdHMgPC0gZnVuY3Rpb24odmFyaWFibGUpIHsNCiAgICANCiAgICBkZiA8LSBjb250X3ZhcnMgJT4lIA0KICAgIGZpbHRlcihncm91cGVkX3ZhciA9PSBwYXN0ZSh2YXJpYWJsZSkpDQogICAgDQogICAgIyBrcnVza2FsLnRlc3QgY2FsY3VsYXRlcyB0aGUga3J1c2thbC1XYWxsaXMgSC1zdGF0aXN0aWMgDQogICAgIyBkb2VzIG5vdCBhc3N1bWUgbm9ybWFsaXR5IGJldHdlZW4gZ3JvdXBzDQogICAgIyBhbHNvIGNhbGxlZCB0aGUgb25lLXdheSBBTk9WQSBvbiByYW5rcw0KICAgIHRlc3QgPSBrcnVza2FsLnRlc3QoZGYkdmFsdWUgfiBkZiR0eXBlKQ0KICAgIA0KICB9DQogIA0KICAjIGNyZWF0ZSBlbXB0eSBsaXN0IGZvciBLcnVza2FsLVdhbGxpcyByZXN1bHRzDQogIGNvbnRfdmFyc19saXN0ID0gbGlzdCgpDQogIA0KICAjIGNyZWF0ZSBsaXN0IG9mIHVuaXF1ZSB2YXJpYWJsZXMNCiAgb3V0Y29tZV9jb250X3ZhcnMgPSB1bmlxdWUoY29udF92YXJzJGdyb3VwZWRfdmFyKQ0KICANCiAgZm9yKGkgaW4gb3V0Y29tZV9jb250X3ZhcnMpIHsNCiAgICANCiAgICB0ZXN0ID0gY29udF92YXJfcmVzdWx0cyhpKQ0KICAgIFgyIDwtIHJvdW5kKHRlc3Qkc3RhdGlzdGljLCA0KQ0KICAgIHBfdmFsdWUgPC0gdGVzdCRwLnZhbHVlDQogICAgZGYgPC0gdGVzdCRwYXJhbWV0ZXINCiAgICBWYXJpYWJsZSA9IHBhc3RlKGkpDQogICAgDQogICAga3cgPC0gY2JpbmQoVmFyaWFibGUsIFgyLCBwX3ZhbHVlLCBkZikgJT4lIA0KICAgICAgZGF0YS5mcmFtZSgpIA0KICAgIA0KICAgIHJvd25hbWVzKGt3KSA8LSBOVUxMDQogICAgDQogICAgY29udF92YXJzX2xpc3RbW2ldXSA8LSBrdw0KICANCiAgICANCiAgfQ0KICANCiAgYW5vdmFfcmVzdWx0cyA8LSBiaW5kX3Jvd3MoY29udF92YXJzX2xpc3QpDQogIA0KICB9DQpgYGANCg0KKipBZHVsdCAmIFBlZGlhdHJpYyBLcnVza2FsbC1XYWxsaXMqKg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KDQprd19jb21iaW5lIDwtIGNvbXB1dGVfa3J1c2thbChjbGluaWNhbF90YWJsZSA9IGNsaW5pY2FsX3RhYmxlX2NvbWJpbmUpDQpkYXRhdGFibGUoa3dfY29tYmluZSAlPiUgYXJyYW5nZShwX3ZhbHVlKSkNCmBgYA0KDQoqKkFkdWx0IENsaW5pY2FsIEtydXNrYWxsLVdhbGxpcyoqDQoNCipOb3RlOiBtZWRpYW4gQ05TIGlzIDAgZm9yIGFsbCBzaXRlcyAocmVhc29uIGZvciB0aGUgTkEgdmFsdWVzKSoNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0Ka3dfYWR1bHQgPC0gY29tcHV0ZV9rcnVza2FsKGNsaW5pY2FsX3RhYmxlID0gY2xpbmljYWxfdGFibGVfYWR1bHQpDQpkYXRhdGFibGUoa3dfYWR1bHQgJT4lIGFycmFuZ2UocF92YWx1ZSkpDQpgYGANCg0KKipQZWRpYXRyaWMgS3J1c2thbGwtV2FsbGlzKioNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCg0Ka3dfcGVkaWF0cmljIDwtIGNvbXB1dGVfa3J1c2thbChjbGluaWNhbF90YWJsZSA9IGNsaW5pY2FsX3RhYmxlX3BlZGlhdHJpYykNCmRhdGF0YWJsZShrd19wZWRpYXRyaWMgJT4lIGFycmFuZ2UocF92YWx1ZSkpDQpgYGANCg0KKiozLiBDb21iaW5lIGNoaS1zcXVhcmUgYW5kIEtydXNrYWwtV2FsbGlzIHJlc3VsdHMqKg0KDQpXZSB3aWxsIGV2YWx1YXRlIHNpZ25pZmljYW5jZSB3aGVuIGNvbnRyb2xsaW5nIGZvciBGYWxzZSBEaXNjb3ZlcnkgUmF0ZSAoRkRSKSANCg0KYGBge3J9DQp0YWJsZW9uZV9zdGF0cyA8LSBmdW5jdGlvbihjaGlzcV9yZXN1bHRzLCBhbm92YV9yZXN1bHRzLCBwb3B1bGF0aW9uKSB7DQoNCiAgZGVtb19zdGF0cyA8LSByYmluZChjaGlzcV9yZXN1bHRzICU+JSBzZWxlY3QoVmFyaWFibGUsIHBfdmFsdWUpLA0KICAgICAgICAgICAgICAgICAgICAgIGFub3ZhX3Jlc3VsdHMgJT4lIHNlbGVjdChWYXJpYWJsZSwgcF92YWx1ZSkpDQogIA0KICAjIGFkanVzdCBwLXZhbHVlcyB3aXRoIEZEUg0KICBkZW1vX3N0YXRzJGFkal9wX3ZhbHVlIDwtIHAuYWRqdXN0KGRlbW9fc3RhdHMkcF92YWx1ZSwgbWV0aG9kID0gImZkciIpDQogIA0KICAjIHJvdW5kICYgZm9ybWF0IHAtdmFsdWUgDQogIGRlbW9fc3RhdHMgPC0gZGVtb19zdGF0cyAlPiUgDQogICAgbXV0YXRlKHBfdmFsdWUgPSBhcy5udW1lcmljKHBfdmFsdWUpLA0KICAgICAgICAgICBwdmFsdWUgPSBpZl9lbHNlKHBfdmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCBwYXN0ZShyb3VuZChwX3ZhbHVlLCAzKSkpLA0KICAgICAgICAgICBhZGoucF92YWx1ZSA9IGlmX2Vsc2UoYWRqX3BfdmFsdWUgPCAwLjAwMSwgIjwgMC4wMDEiLCBwYXN0ZShyb3VuZChhZGpfcF92YWx1ZSwgMykpKSkNCiAgDQogICMgdGlkeSB1cCB0aGUgZGVtb2dyYXBoaWNzIHN0YXQgdGFibGUNCiAgZGVtb19zdGF0c190aWR5IDwtIGRlbW9fc3RhdHMgJT4lIA0KICAgIHJlbmFtZShgVW5hZGp1c3RlZCBQLXZhbHVlIChyYXcpYCA9IHBfdmFsdWUsDQogICAgICAgICAgIGBGRFIgQWRqdXN0ZWQgUC12YWx1ZSAocmF3KWAgPSBhZGpfcF92YWx1ZSwNCiAgICAgICAgICAgYFVuYWRqdXN0ZWQgUC12YWx1ZSAodGlkeSlgID0gcHZhbHVlLA0KICAgICAgICAgICBgRkRSIEFkanVzdGVkIFAtdmFsdWUgKHRpZHkpYCA9IGFkai5wX3ZhbHVlKSAlPiUgDQogICAgc2VsZWN0KFZhcmlhYmxlLCBgVW5hZGp1c3RlZCBQLXZhbHVlIChyYXcpYCwgYEZEUiBBZGp1c3RlZCBQLXZhbHVlIChyYXcpYCwgYFVuYWRqdXN0ZWQgUC12YWx1ZSAodGlkeSlgLCBgRkRSIEFkanVzdGVkIFAtdmFsdWUgKHRpZHkpYCkNCg0KICB3cml0ZS5jc3YoZGVtb19zdGF0c190aWR5LCBwYXN0ZTAoInRhYmxlcy9UYWJsZTFfcHZhbHNfIiwgcG9wdWxhdGlvbiwgIi5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpDQogIA0KICByZXR1cm4oZGVtb19zdGF0c190aWR5KQ0KICANCn0NCmBgYA0KDQoqKkluY29ycG9yYXRlIENvbW9yYmlkaXRpZXMqKg0KDQpgYGB7cn0NCiMgZGVmaW5lIG1hdHJpeCBmb3IgZWFjaCBjb21vcmJpZGl0eQ0KY29tb3JiX21hdHJpeCA8LSBmdW5jdGlvbihjb21vcmJpZGl0eV90YWJsZSwgY29tb3JiaWRpdHksIHBvcHVsYXRpb24pIHsNCg0KICAgIGlmKHBvcHVsYXRpb249PSdDb21iaW5lZCcpIHsNCiAgICAgIGNvbW9yYmlkaXR5X3RhYmxlID0gY29tb3JiaWRpdHlfdGFibGUNCiAgICB9IGVsc2UgaWYgKHBvcHVsYXRpb249PSJBZHVsdCIpIHsNCiAgICAgIGNvbW9yYmlkaXR5X3RhYmxlID0gY29tb3JiaWRpdHlfdGFibGUgJT4lIA0KICAgICAgICBmaWx0ZXIocG9wdWxhdGlvbiA9PSAiQWR1bHQiKQ0KICAgIH0gZWxzZSBpZiAocG9wdWxhdGlvbj09IlBlZGlhdHJpYyIpIHsNCiAgICAgIGNvbW9yYmlkaXR5X3RhYmxlID0gY29tb3JiaWRpdHlfdGFibGUgJT4lIA0KICAgICAgICBmaWx0ZXIocG9wdWxhdGlvbiA9PSAiUGVkaWF0cmljIikNCiAgICB9IGVsc2Ugew0KICAgICAgcHJpbnQoJ2luY29ycmVjdCBwb3B1bGF0aW9uIHNwZWNpZmljYXRpb24nKQ0KICAgIH0NCiAgDQogIG1hdF9jb21vcmIgPC0gY29tb3JiaWRpdHlfdGFibGUgJT4lDQogICAgZmlsdGVyKENvbW9yYmlkaXR5ID09IHBhc3RlKGNvbW9yYmlkaXR5KSkgJT4lDQogICAgZ3JvdXBfYnkoQ29tb3JiaWRpdHkpICU+JSANCiAgICBtdXRhdGUoTm9uZV9jb21vcmIgPSBzdW0oTm9uZV9Ub3RhbCwgbmEucm0gPSBUUlVFKSwNCiAgICBDTlNfY29tb3JiID0gc3VtKENOU19Ub3RhbCwgbmEucm0gPSBUUlVFKSwNCiAgICBQTlNfY29tb3JiID0gc3VtKFBOU19Ub3RhbCwgbmEucm0gPSBUUlVFKSkgJT4lDQogICAgdW5ncm91cCgpICU+JQ0KICAgIGRpc3RpbmN0KE5vbmVfY29tb3JiLCBDTlNfY29tb3JiLCBQTlNfY29tb3JiKSAlPiUNCiAgICBtdXRhdGUobm9uZV9kaWYgPSBuZXVyb19wdF9jb3VudHNfc3VtJE5vbmVfbiAtIE5vbmVfY29tb3JiLA0KICAgIHBuc19kaWYgPSBuZXVyb19wdF9jb3VudHNfc3VtJFBOU19uIC0gUE5TX2NvbW9yYiwNCiAgICBjbnNfZGlmID0gbmV1cm9fcHRfY291bnRzX3N1bSRDTlNfbiAtIENOU19jb21vcmIpICU+JQ0KICAgIGRhdGEuZnJhbWUoKSAlPiUNCiAgICAjYXMuaW50ZWdlcigpICU+JQ0KICAgIG1hdHJpeChucm93ID0gMiwgbmNvbCA9IDMsIGJ5cm93ID0gVFJVRSkNCg0KICBjb21vcmJfcmVzdWx0cyA8LSBjaGlzcS50ZXN0KHVubGlzdChtYXRfY29tb3JiKSkNCiAgDQogIHJlc3VsdHNfZGYgPC0gZGF0YS5mcmFtZShWYXJpYWJsZSA9IHBhc3RlKGNvbW9yYmlkaXR5KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWDIgPSByb3VuZChjb21vcmJfcmVzdWx0cyRzdGF0aXN0aWMsIDQpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwX3ZhbHVlID0gY29tb3JiX3Jlc3VsdHMkcC52YWx1ZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGYgPSBjb21vcmJfcmVzdWx0cyRwYXJhbWV0ZXIpDQogIHJvdy5uYW1lcyhyZXN1bHRzX2RmKSA8LSBOVUxMDQogIA0KICByZXR1cm4ocmVzdWx0c19kZikNCn0NCg0KIyMgY29tYmluZWQgY29tb3JiaWRpdGllcw0KY29tb3JiaWRpdHlfY29tYmluZWRfY2hpX2xpc3QgPC0gbGlzdCgpDQoNCmZvcihpIGluIHRvcF9jb21vcmJfYWR1bHRzKSB7DQogIA0KICBjb21vcmJfY2hpX3Jlc3VsdHMgPC0gY29tb3JiX21hdHJpeChjb21vcmJpZGl0eV90YWJsZSA9IGNvbW9yYl90YWJsZV93aWRlLCBpLCBwb3B1bGF0aW9uID0gIkNvbWJpbmVkIikNCiAgY29tb3JiaWRpdHlfY29tYmluZWRfY2hpX2xpc3RbW2ldXSA8LSBjb21vcmJfY2hpX3Jlc3VsdHMNCn0NCg0KY29tb3JiaWRpdHlfY29tYmluZWRfY2hpIDwtIGNvbW9yYmlkaXR5X2NvbWJpbmVkX2NoaV9saXN0ICU+JQ0KICBiaW5kX3Jvd3MoKSANCg0KY2hpc3FfY29tYmluZV9jb21vcmIgPC0gY2hpc3FfY29tYmluZSAlPiUNCiAgbXV0YXRlKFgyID0gYXMubnVtZXJpYyhYMiksDQogIGRmID0gYXMubnVtZXJpYyhkZikpICU+JQ0KICBiaW5kX3Jvd3MoY29tb3JiaWRpdHlfY29tYmluZWRfY2hpKSANCg0KDQoNCiMjIGFkdWx0IGNvbW9yYmlkaXRpZXMNCmNvbW9yYmlkaXR5X2FkdWx0X2NoaV9saXN0IDwtIGxpc3QoKQ0KDQpmb3IoaSBpbiB0b3BfY29tb3JiX2FkdWx0cykgew0KICANCiAgY29tb3JiX2NoaV9yZXN1bHRzIDwtIGNvbW9yYl9tYXRyaXgoY29tb3JiaWRpdHlfdGFibGUgPSBjb21vcmJfdGFibGVfd2lkZSwgaSwgcG9wdWxhdGlvbiA9ICJBZHVsdCIpDQogIGNvbW9yYmlkaXR5X2FkdWx0X2NoaV9saXN0W1tpXV0gPC0gY29tb3JiX2NoaV9yZXN1bHRzDQp9DQoNCmNvbW9yYmlkaXR5X2FkdWx0X2NoaSA8LSBjb21vcmJpZGl0eV9hZHVsdF9jaGlfbGlzdCAlPiUNCiAgYmluZF9yb3dzKCkgDQoNCmNoaXNxX2FkdWx0X2NvbW9yYiA8LSBjaGlzcV9hZHVsdCAlPiUNCiAgbXV0YXRlKFgyID0gYXMubnVtZXJpYyhYMiksDQogIGRmID0gYXMubnVtZXJpYyhkZikpICU+JQ0KICBiaW5kX3Jvd3MoY29tb3JiaWRpdHlfYWR1bHRfY2hpKSANCg0KIyMgcGVkaWF0cmljIGNvbW9yYmlkaXRpZXMNCmNvbW9yYmlkaXR5X3BlZGlhdHJpY19jaGlfbGlzdCA8LSBsaXN0KCkNCg0KZm9yKGkgaW4gdG9wX2NvbW9yYl9wZWRpYXRyaWNzKSB7DQogIA0KICBjb21vcmJfY2hpX3Jlc3VsdHMgPC0gY29tb3JiX21hdHJpeChjb21vcmJpZGl0eV90YWJsZSA9IGNvbW9yYl90YWJsZV93aWRlLCBpLCBwb3B1bGF0aW9uID0gIlBlZGlhdHJpYyIpDQogIGNvbW9yYmlkaXR5X3BlZGlhdHJpY19jaGlfbGlzdFtbaV1dIDwtIGNvbW9yYl9jaGlfcmVzdWx0cw0KfQ0KDQpjb21vcmJpZGl0eV9wZWRpYXRyaWNfY2hpIDwtIGNvbW9yYmlkaXR5X3BlZGlhdHJpY19jaGlfbGlzdCAlPiUNCiAgYmluZF9yb3dzKCkgDQoNCmNoaXNxX3BlZGlhdHJpY19jb21vcmIgPC0gY2hpc3FfcGVkaWF0cmljICU+JQ0KICBtdXRhdGUoWDIgPSBhcy5udW1lcmljKFgyKSwNCiAgZGYgPSBhcy5udW1lcmljKGRmKSkgJT4lDQogIGJpbmRfcm93cyhjb21vcmJpZGl0eV9wZWRpYXRyaWNfY2hpX2xpc3QpIA0KYGBgDQoNCiMjIFRhYmxlIE9uZSAtIENvbWJpbmVkIEFuYWx5c2lzDQoNCmBgYHtyfQ0KY29tYmluZV90YWJsZW9uZV9zdGF0cyA8LSB0YWJsZW9uZV9zdGF0cyhjaGlzcV9yZXN1bHRzID0gY2hpc3FfY29tYmluZV9jb21vcmIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFub3ZhX3Jlc3VsdHMgPSBrd19jb21iaW5lLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uID0gImNvbWJpbmVkIikgDQogIA0KDQpkYXRhdGFibGUoY29tYmluZV90YWJsZW9uZV9zdGF0cykNCmBgYA0KDQoNCiMjIFRhYmxlIE9uZSAtIEFkdWx0IEFuYWx5c2lzDQoNCmBgYHtyfQ0KYWR1bHRfdGFibGVvbmVfc3RhdHMgPC0gdGFibGVvbmVfc3RhdHMoY2hpc3FfcmVzdWx0cyA9IGNoaXNxX2FkdWx0X2NvbW9yYiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5vdmFfcmVzdWx0cyA9IGt3X2FkdWx0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uID0gImFkdWx0IikgDQogIA0KDQpkYXRhdGFibGUoYWR1bHRfdGFibGVvbmVfc3RhdHMpDQpgYGANCg0KIyMgVGFibGUgT25lIC0gUGVkaWF0cmljIEFuYWx5c2lzDQoNCmBgYHtyfQ0KcGVkaWF0cmljX3RhYmxlb25lX3N0YXRzIDwtIHRhYmxlb25lX3N0YXRzKGNoaXNxX3Jlc3VsdHMgPSBjaGlzcV9wZWRpYXRyaWNfY29tb3JiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbm92YV9yZXN1bHRzID0ga3dfcGVkaWF0cmljLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb3B1bGF0aW9uID0gInBlZGlhdHJpYyIpIA0KICANCg0KZGF0YXRhYmxlKHBlZGlhdHJpY190YWJsZW9uZV9zdGF0cykNCmBgYA0KDQojICoqTmV1cm9sb2dpY2FsIERpYWdub3NpcyBBbmFseXNpcyoqDQoNCkV2YWx1YXRlIENOUyBhbmQgUE5TIG5ldXJvbG9naWNhbCBkaWFnbm9zZXMgaW4gb3VyIHBhdGllbnQgY29ob3J0DQoNCioqRm9ybWF0IERpYWdub3N0aWMgY29kZSBkYXRhKioNCg0KYGBge3IgZmlnLndpZHRoPTEyfQ0KIyBpbXBvcnQgbGlzdCBvZiBJQ0QtOS9JQ0QtMTAgY29kZXMNCm5ldXJvX2ljZHNfMTAgPC0NCiAgcmVhZHhsOjpyZWFkX2V4Y2VsKCJwdWJsaWMtZGF0YS8yMDIwLTAyLTIyX25ldXJvLWljZDEwX0NOU3ZQTlMueGxzeCIsIHNoZWV0ID0gMikgJT4lIA0KICByZW5hbWUoDQogICAgImljZCIgPSBgSUNELTEwYCwNCiAgICAicG5zX2NucyIgPSBgTmVydm91cyBzeXN0ZW0gSW52b2x2ZW1lbnQgKDE9Y2VudHJhbCwgMj1wZXJpcGhlcmFsLCAzPWlycmVsZXZhbnQpYCwNCiAgICAidHlwZSIgPSBgSUNELTEwX3R5cGUgKDE9Zmlyc3QgdGhyZWUgYWxwaGFudW1lcmljIGNvZGUgb25seSwgMj1kaWdpdHMgYWZ0ZXIgZGVjaW1hbCBwb2ludClgKSAlPiUNCiAgZmlsdGVyKHR5cGUgPT0gMSkgJT4lIA0KICBtdXRhdGUocG5zX2NucyA9IGFzLmZhY3RvcihwbnNfY25zKSAlPiUgZmN0X3JlY29kZSgNCiAgICBDZW50cmFsID0gIjEiLA0KICAgIFBlcmlwaGVyYWwgPSAiMiINCiAgKSkgJT4lDQogIGRpc3RpbmN0KGljZCwgYE5ldXJvbG9naWNhbCBEaXNlYXNlIENhdGVnb3J5YCwgcG5zX2NucywgYElDRC0xMCBEZXNjcmlwdGlvbiBSZXZpc2VkYCkgJT4lDQogIHJlbmFtZSgiZGVzY3JpcHRpb24iID0gYElDRC0xMCBEZXNjcmlwdGlvbiBSZXZpc2VkYCkgJT4lDQogIG11dGF0ZShjb25jZXB0X3R5cGUgPSAiaWNkLTEwIikNCg0KbmV1cm9faWNkc185IDwtIHJlYWQuY3N2KCJwdWJsaWMtZGF0YS9pY2Q5X3RhYl9DTlN2UE5TLmNzdiIpICU+JQ0KICByZW5hbWUoDQogICAgIk5ldXJvbG9naWNhbCBEaXNlYXNlIENhdGVnb3J5IiA9ICJOZXVyb2xvZ2ljYWwuRGlzZWFzZS5DYXRlZ29yeSIsDQogICAgInBuc19jbnMiID0gYE5lcnZvdXMuc3lzdGVtLkludm9sdmVtZW50Li4xLmNlbnRyYWwuLjIucGVyaXBoZXJhbC5gLA0KICAgICJpY2RfZGVzY3JpcHRpb24iID0gYGljZDlfZGVzY19yZXZpc2VkYA0KICApICU+JQ0KICBtdXRhdGUoDQogICAgcG5zX2NucyA9IGFzLmZhY3RvcihwbnNfY25zKSAlPiUgZmN0X3JlY29kZSgNCiAgICAgIENlbnRyYWwgPSAiMSIsDQogICAgICBQZXJpcGhlcmFsID0gIjIiDQogICAgKSwNCiAgICBjb25jZXB0X3R5cGUgPSAiRElBRy1JQ0Q5Ig0KICApICU+JQ0KICBzZWxlY3QoaWNkLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgLCBwbnNfY25zLCBpY2RfZGVzY3JpcHRpb24pICU+JQ0KICByZW5hbWUoImRlc2NyaXB0aW9uIiA9IGljZF9kZXNjcmlwdGlvbikgJT4lDQogIG11dGF0ZShjb25jZXB0X3R5cGUgPSAiaWNkLTkiKQ0KDQojIGJpbmQgbGlzdCBvZiBJQ0QtOS9JQ0QtMTAgY29kZXMNCmljZHMgPC0gcmJpbmQobmV1cm9faWNkc18xMCwgbmV1cm9faWNkc185KQ0KYGBgDQoNCioqQ2FsY3VsYXRlIHRvdGFsIG51bWJlciBvZiBuZXVybyBkaWFnbm9zZXMgZm9yIGVhY2ggYWR1bHRzICYgcGVkaWF0cmljcyoqDQoNCmBgYHtyfQ0KY29tcHV0ZV9uZXVyb19jb3VudHMgPC0gZnVuY3Rpb24oc29ydGVkX3NpdGVzLCBzYW1wbGVfc2l6ZSwgaXNfcGVkaWF0cmljID0gRkFMU0UpIHsNCiAgDQogICAgIyBjcmVhdGUgZW1wdHkgbGlzdCB0byBzYXZlIHByb2Nlc3NlZCBkaWFnbm9zdGljIGRhdGENCiAgICBkaWFnX3RhYmxlX2xpc3QgPC0gbGlzdCgpDQogIA0KICAgIGlmKGlzX3BlZGlhdHJpYz09RkFMU0UpIHsNCiAgICAgIHBvcHVsYXRpb24gPSAiYWR1bHRzIg0KICAgICAgfSBlbHNlIHsNCiAgICAgIHBvcHVsYXRpb24gPSAicGVkaWF0cmljcyINCiAgICAgIH0NCiAgDQogICMgZm9yIGVhY2ggaGVhbHRoY2FyZSBzeXN0ZW0sIHdlIHdpbGwgY2FsY3VsYXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgcGF0aWVudHMgd2l0aCBlYWNoIElDRCBjb2RlDQogIGZvciAoaSBpbiBzb3J0ZWRfc2l0ZXMpIHsNCiAgICB0bXAgPC0gZ2V0KGkpDQogICAgdG1wX2RpYWcgPC0gdG1wW1tjKA0KICAgICAgImZpcnN0X2hvc3BfcmVzdWx0cyIsDQogICAgICAiaWNkX3RhYmxlcyIsDQogICAgICBwYXN0ZTAoImljZF90YWJsZXNfIiwgcG9wdWxhdGlvbiksDQogICAgICAiZGVtb190YWJsZSINCiAgICApXV0NCiAgICB0bXBfZGlhZyRzaXRlIDwtIHRtcFtbInNpdGUiXV0NCiAgICBkaWFnX3RhYmxlX2xpc3RbW2ldXSA8LSB0bXBfZGlhZyAlPiUNCiAgICAgIGZpbHRlcih2YXJpYWJsZSA9PSAic2V4LmZlbWFsZSIgfCB2YXJpYWJsZSA9PSAic2V4Lm1hbGUiIHwgdmFyaWFibGUgPT0gInNleC5vdGhlciIpICU+JQ0KICAgICAgc2VsZWN0KHNpdGUsIGNvbnRhaW5zKCJuX3ZhciIpKQ0KICB9DQogIA0KICBkaWFnX3RhYmxlX3dpZGUgPC0gYmluZF9yb3dzKGRpYWdfdGFibGVfbGlzdCkNCiAgDQogICMgd2Ugd2lsbCBjb3VudCB0aGUgdG90YWwgbnVtYmVyIGFuZCBwZXJjZW50IGFjcm9zcyBoZWFsdGhjYXJlIHN5c3RlbXMNCiAgZGlhZ19jb3VudHMgPC0gY29sU3VtcyhGaWx0ZXIoaXMubnVtZXJpYywgZGlhZ190YWJsZV93aWRlKSwgbmEucm0gPSBUUlVFKSAlPiUgDQogICAgZGF0YS5mcmFtZSgpICU+JSANCiAgICBtdXRhdGUocGVyYyA9IHJvdW5kKC4vc2FtcGxlX3NpemUgKiAxMDAsIDEpKQ0KICANCiAgIyBmb3JtYXQgZGlhZ25vc3RpYyB0YWJsZQ0KICBkaWFnX2NvdW50cyA8LSB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbihkaWFnX2NvdW50cywgImljZCIpICU+JQ0KICAgIG11dGF0ZShpY2QgPSBnc3ViKCJuX3Zhcl8iLCAiIiwgaWNkKSkgJT4lDQogICAgcmVuYW1lKGBOdW1iZXIgb2YgUGF0aWVudHNgID0gIi4iKSAlPiUNCiAgICBmdWxsX2pvaW4oLiwgaWNkcywgYnkgPSAiaWNkIikgJT4lDQogICAgZHBseXI6OmFycmFuZ2UoZGVzYyhgTnVtYmVyIG9mIFBhdGllbnRzYCkpICU+JSANCiAgICBtdXRhdGUoYE51bWJlciBvZiBQYXRpZW50cyAoJSBvZiBDb2hvcnQpYCA9IHBhc3RlKGBOdW1iZXIgb2YgUGF0aWVudHNgLCAiKCIsIHBlcmMsICIpIiwgc2VwID0gIiAiKSkgJT4lIA0KICAgIHNlbGVjdChwbnNfY25zLCBgTmV1cm9sb2dpY2FsIERpc2Vhc2UgQ2F0ZWdvcnlgLCBjb25jZXB0X3R5cGUsIA0KICAgICAgICAgICBpY2QsIGRlc2NyaXB0aW9uLCBgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgKSAlPiUgDQogICAgICByZW5hbWUoQ29kZSA9ICJpY2QiLA0KICAgICAgICAgICBgTmVydm91cyBTeXN0ZW1gID0gInBuc19jbnMiLA0KICAgICAgICAgICBEZXNjcmlwdGlvbiA9ICJkZXNjcmlwdGlvbiIsDQogICAgICAgICAgIGBJQ0QgVmVyc2lvbmAgPSAiY29uY2VwdF90eXBlIikgJT4lIA0KICAgIG11dGF0ZShgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgID0gaWZfZWxzZShgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgID09ICJOQSAoIE5BICkiLCAiMCAoIDAgKSIsIGBOdW1iZXIgb2YgUGF0aWVudHMgKCUgb2YgQ29ob3J0KWApLA0KICAgICAgICAgICBDb2RlID0gaWZfZWxzZShDb2RlID09ICJOTiIsICJObyBOZXVyb2xvZ2ljYWwgRGlzZWFzZSAoTk5DKSIsIENvZGUpKQ0KICANCiAgcmV0dXJuKGRpYWdfY291bnRzKQ0KICANCiAgfQ0KDQpgYGANCg0KYGBge3J9DQpuZXVyb19jb3VudHNfYWR1bHQgPC0gY29tcHV0ZV9uZXVyb19jb3VudHMoc29ydGVkX3NpdGVzID0gYWR1bHRfc2l0ZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX3NpemUgPSBuX2FkdWx0LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc19wZWRpYXRyaWMgPSBGQUxTRSkNCg0KbmV1cm9fY291bnRzX3BlZGlhdHJpYyA8LSBjb21wdXRlX25ldXJvX2NvdW50cyhzb3J0ZWRfc2l0ZXMgPSBwZWRpYXRyaWNfc2l0ZXMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlX3NpemUgPSBuX3BlZGlhdHJpYywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNfcGVkaWF0cmljID0gVFJVRSkNCg0KbmV1cm9fY291bnRzX2NvbWJpbmVkIDwtIG5ldXJvX2NvdW50c19hZHVsdCAlPiUgDQogIHJlbmFtZShgTnVtYmVyIG9mIEFkdWx0IFBhdGllbnRzICglIG9mIENvaG9ydClgID0gYE51bWJlciBvZiBQYXRpZW50cyAoJSBvZiBDb2hvcnQpYCkgJT4lIA0KICBsZWZ0X2pvaW4oLiwgbmV1cm9fY291bnRzX3BlZGlhdHJpYyAlPiUgDQogICAgICAgICAgICAgIHNlbGVjdChgSUNEIFZlcnNpb25gLCBDb2RlLCBgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgKSAlPiUgDQogICAgICAgICAgICAgIHJlbmFtZSgiTnVtYmVyIG9mIFBlZGlhdHJpYyBQYXRpZW50cyAoJSBvZiBDb2hvcnRgKSIgPSBgTnVtYmVyIG9mIFBhdGllbnRzICglIG9mIENvaG9ydClgKSkNCg0Kd3JpdGUuY3N2KG5ldXJvX2NvdW50c19jb21iaW5lZCwgcGFzdGUwKCJ0YWJsZXMvU3VwcC5UYWJsZTJfZGlhZ25vc2VzIiwgIi5jc3YiKSwgcm93Lm5hbWVzID0gRkFMU0UpDQogIA0KYGBgDQoNCmBgYHtyfQ0KZGF0YXRhYmxlKG5ldXJvX2NvdW50c19hZHVsdCwgY2FwdGlvbiA9ICJOdW1iZXIgKCUpIG9mIEFkdWx0IFBhdGllbnRzIHdpdGggZWFjaCBOZXVyb2xvZ2ljYWwgRGlhZ25vc2lzIikgDQpgYGANCg0KYGBge3J9DQpkYXRhdGFibGUobmV1cm9fY291bnRzX3BlZGlhdHJpYywgY2FwdGlvbiA9ICJOdW1iZXIgKCUpIG9mIFBlZGlhdHJpYyBQYXRpZW50cyB3aXRoIGVhY2ggTmV1cm9sb2dpY2FsIERpYWdub3NpcyIpIA0KYGBgDQoNCiMjIEV2YWx1YXRlIERpYWdub3NlcyBieSBBZ2UgJiBPdXRjb21lDQoNCmBgYHtyfQ0KY29tcHV0ZV9zdHJhdGlmaWVkX25ldXJvX2NvdW50cyA8LSBmdW5jdGlvbih0YWJsZW9uZSwgc29ydGVkX3NpdGVzLCBpc19wZWRpYXRyaWMgPSBGQUxTRSkgew0KICANCiAgIyBjcmVhdGUgZW1wdHkgbGlzdCB0byBzYXZlIHByb2Nlc3NlZCBkaWFnbm9zdGljIGRhdGENCiAgICBkaWFnX3RhYmxlX2xpc3QgPC0gbGlzdCgpDQogIA0KICAgIGlmKGlzX3BlZGlhdHJpYz09RkFMU0UpIHsNCiAgICAgIHBvcHVsYXRpb24gPSAiYWR1bHRzIg0KICAgICAgfSBlbHNlIHsNCiAgICAgIHBvcHVsYXRpb24gPSAicGVkaWF0cmljcyINCiAgICAgIH0NCiAgDQogICMgZm9yIGVhY2ggaGVhbHRoY2FyZSBzeXN0ZW0sIHdlIHdpbGwgY2FsY3VsYXRlIHRoZSB0b3RhbCBudW1iZXIgb2YgcGF0aWVudHMgd2l0aCBlYWNoIElDRCBjb2RlDQogIGZvciAoaSBpbiBzb3J0ZWRfc2l0ZXMpIHsNCiAgICAjcHJpbnQoaSkNCiAgICB0bXAgPC0gZ2V0KGkpDQogICAgdG1wX2RpYWcgPC0gdG1wW1tjKA0KICAgICAgImZpcnN0X2hvc3BfcmVzdWx0cyIsDQogICAgICAiaWNkX3RhYmxlcyIsDQogICAgICBwYXN0ZTAoImljZF90YWJsZXNfIiwgcG9wdWxhdGlvbiksDQogICAgICAiZGVtb190YWJsZSINCiAgICApXV0NCiAgICB0bXBfZGlhZyRzaXRlIDwtIHRtcFtbInNpdGUiXV0NCiAgICB0cnkoDQogICAgZGlhZ190YWJsZV9saXN0W1tpXV0gPC0gdG1wX2RpYWcgJT4lDQogICAgICBzZWxlY3Qoc2l0ZSwgdmFyaWFibGUsIHN0YXJ0c193aXRoKCJuX3ZhciIpKSAlPiUgDQogICAgICBwaXZvdF9sb25nZXIoY29scyA9IHN0YXJ0c193aXRoKCJuX3ZhciIpLCBuYW1lc190byA9ICJjb2RlIiwgdmFsdWVzX3RvID0gImZyZXEiKSAlPiUgDQogICAgICB1bmdyb3VwKCkNCiAgICApDQogIH0NCg0KICBkaWFnX3RhYmxlX3dpZGVfYWxsIDwtIGJpbmRfcm93cyhkaWFnX3RhYmxlX2xpc3QpICU+JSANCiAgICAgIGdyb3VwX2J5KHZhcmlhYmxlLCBjb2RlKSAlPiUgDQogICAgIyB0b3RhbCBudW1iZXIgb2YgcGF0aWVudHMgd2hvIGhhZCBlYWNoIGNvZGUgZm9yIGVhY2ggdmFyaWFibGUgYWNyb3NzIHNpdGVzDQogICAgICBtdXRhdGUodG90YWxfbl92YXIgID0gc3VtKGZyZXEsIG5hLnJtID0gVFJVRSkpICU+JSANCiAgICBkaXN0aW5jdCh2YXJpYWJsZSwgY29kZSwgdG90YWxfbl92YXIpICU+JSANCiAgICBtdXRhdGUoY29kZSA9IGdzdWIoIm5fdmFyXyIsICIiLCBjb2RlKSwNCiAgICAgICAgICAgdmFyaWFibGUgPSBnc3ViKCJTZXZlcml0eS4iLCAiIiwgdmFyaWFibGUpLA0KICAgICAgICAgICB2YXJpYWJsZSA9IGdzdWIoIlN1cnZpdmFsLiIsICIiLCB2YXJpYWJsZSksDQogICAgICAgICAgIHZhcmlhYmxlID0gZ3N1YigicmVhZG1pdHRlZCIsICIiLCB2YXJpYWJsZSksDQogICAgICAgICAgIHZhcmlhYmxlID0gZ3N1YigiYWdlX2dyb3VwIiwgIiIsIHZhcmlhYmxlKSwNCiAgICAgICAgICAgdmFyaWFibGUgPSBnc3ViKCJjb3ZpZF9kaXNjaGFyZ2VkIiwgIiIsIHZhcmlhYmxlKSwNCiAgICAgICAgICAgdmFyaWFibGUgPSBpZl9lbHNlKHZhcmlhYmxlID09ICIuVFJVRSIsICJSZWFkbWl0dGVkIiwgdmFyaWFibGUpKSAlPiUgDQogICAgbGVmdF9qb2luKC4sIGljZHMgJT4lIHJlbmFtZShjb2RlID0gImljZCIpLCBieSA9ICJjb2RlIikgJT4lIA0KICAgIHVuZ3JvdXAoKSAlPiUgDQogICAgZmlsdGVyKCFjb25jZXB0X3R5cGUgPT0gImljZC05IikNCg0KDQogICMgcmVmYWN0b3IgbmV1cm8gc3RhdHVzDQogIGRpYWdfdGFibGVfd2lkZV9hbGwkcG5zX2NucyA8LSBmYWN0b3IoZGlhZ190YWJsZV93aWRlX2FsbCRwbnNfY25zLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscz1jKCJDZW50cmFsIiwgIlBlcmlwaGVyYWwiKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiQ05TIiwgIlBOUyIpKQ0KICANCiAgDQogIHJldHVybihkaWFnX3RhYmxlX3dpZGVfYWxsKQ0KICANCiAgfQ0KYGBgDQoNCmBgYHtyfQ0Kc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWR1bHQgPC0gY29tcHV0ZV9zdHJhdGlmaWVkX25ldXJvX2NvdW50cyhzb3J0ZWRfc2l0ZXMgPSBhZHVsdF9zaXRlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNfcGVkaWF0cmljID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhYmxlb25lID0gdGFibGVvbmVfYWR1bHQpDQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX3BlZGlhdHJpYyA8LSBjb21wdXRlX3N0cmF0aWZpZWRfbmV1cm9fY291bnRzKHNvcnRlZF9zaXRlcyA9IHBlZGlhdHJpY19zaXRlcywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaXNfcGVkaWF0cmljID0gVFJVRSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFibGVvbmUgPSB0YWJsZW9uZV9wZWRpYXRyaWMpDQpgYGANCg0KDQpgYGB7cn0NCmNvbXB1dGVfc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWdlIDwtIGZ1bmN0aW9uKHN0cmF0aWZpZWRfbmV1cm9fY291bnRzLCB0YWJsZW9uZSkgew0KICANCiAgc3RyYXRpZmllZF9uZXVyb19jb3VudHMkdmFyaWFibGUgPC0gZmFjdG9yKHN0cmF0aWZpZWRfbmV1cm9fY291bnRzJHZhcmlhYmxlLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygiLjAwdG8wMiIsICIuMDN0bzA1IiwgIi4wNnRvMTEiLCAiLjEydG8xNyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIi4xOHRvMjUiLCAiLjI2dG80OSIsICIuNTB0bzY5IiwgIi43MHRvNzkiLCAiLjgwcGx1cyIpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiMC0yIFllYXJzIiwgIjMtNSBZZWFycyIsICI2LTExIFllYXJzIiwgIjEyLTE3IFllYXJzIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTgtMjUgWWVhcnMiLCAiMjYtNDkgWWVhcnMiLCAiNTAtNjkgWWVhcnMiLCAiNzAtNzkgWWVhcnMiLCAiODArIFllYXJzIikpDQogIA0KICAjIHRvdGFsIGNvdW50cw0KICBuXzBfMiA8LSBnZXRfdGFibGVfbih0YWJsZW9uZSwgdmFyID0gIjAtMiIpDQogIG5fM181ID0gZ2V0X3RhYmxlX24odGFibGVvbmUsIHZhciA9ICIzLTUiKQ0KICBuXzZfMTEgPSBnZXRfdGFibGVfbih0YWJsZW9uZSwgdmFyID0gIjYtMTEiKQ0KICBuXzEyXzE3ID0gZ2V0X3RhYmxlX24odGFibGVvbmUsIHZhciA9ICIxMi0xNyIpDQogIG5fMThfMjUgPSBnZXRfdGFibGVfbih0YWJsZW9uZSwgdmFyID0gIjE4LTI1IikNCiAgbl8yNl80OSA9IGdldF90YWJsZV9uKHRhYmxlb25lLCB2YXIgPSAiMjYtNDkiKQ0KICBuXzUwXzY5ID0gZ2V0X3RhYmxlX24odGFibGVvbmUsIHZhciA9ICI1MC02OSIpDQogIG5fNzBfNzkgPSBnZXRfdGFibGVfbih0YWJsZW9uZSwgdmFyID0gIjcwLTc5IikNCiAgbl84MCA9IGdldF90YWJsZV9uKHRhYmxlb25lLCB2YXIgPSAiODArIikNCiAgDQogIGFnZV9kZiA8LSBkYXRhLmZyYW1lKCJ2YXJpYWJsZSIgPSBjKCIwLTIgWWVhcnMiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjMtNSBZZWFycyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjYtMTEgWWVhcnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMi0xNyBZZWFycyIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTgtMjUgWWVhcnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyNi00OSBZZWFycyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjUwLTY5IFllYXJzIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNzAtNzkgWWVhcnMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI4MCsgWWVhcnMiKSwNCiAgICAgICAgICAgICAgICAgICAgICJuX3ZhciIgPSBjKG5fMF8yLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fM181LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fNl8xMSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuXzEyXzE3LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fMThfMjUsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl8yNl80OSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuXzUwXzY5LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5fNzBfNzksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbl84MCkpDQoNCiAgDQogIHN0cmF0aWZpZWRfbmV1cm9fY291bnRzIDwtIHN0cmF0aWZpZWRfbmV1cm9fY291bnRzICU+JSANCiAgICBmaWx0ZXIoZ3JlcGwoIlllYXJzIiwgdmFyaWFibGUpLA0KICAgICAgICAgICAgICAgICFjb2RlID09ICJOTiIsIA0KICAgICAgICAgICAgICAgICF0b3RhbF9uX3ZhciA9PSAwKSAlPiUgDQogICAgICAgICAjIHRvdGFsX24gd291bGQgYmUgY2FsY3VsYXRpbmcgdG90YWwgY29kZXMgYWNyb3NzIGVhY2ggYWdlIGdyb3VwIHdoaWNoIGlzIG5vdCB0cnVlIHJlZmxlY3Rpb24gb2YgcGF0aWVudCBjb3VudHMgYi9jIHBhdGllbnRzIGNhbiBoYXZlIG1vcmUgdGhhbiBvbmUgY29kZQ0KICAgICAgICAgI211dGF0ZSh0b3RhbF9uID0gc3VtKHRvdGFsX25fdmFyKSkgJT4lIA0KICAgICAgICAgdW5ncm91cCgpICU+JSANCiAgICAgICAgIGxlZnRfam9pbiguLCBhZ2VfZGYsIGJ5ID0gInZhcmlhYmxlIikgJT4lIA0KICAgICAgICAgbXV0YXRlKG5fdmFyID0gYXMubnVtZXJpYyhuX3ZhciksDQogICAgICAgICAgICAgICAgcGVyYyA9IGFzLmNoYXJhY3Rlcihyb3VuZCh0b3RhbF9uX3Zhci9uX3ZhcioxMDAsMSkpLA0KICAgICAgICAgICAgICAgIHBlcmMgPSBpZl9lbHNlKHBlcmMgPCAwLjEsICc8MC4xJywgcGVyYyksDQogICAgICAgICAgICAgICAgcGVyYyA9IHBhc3RlMChwZXJjLCAiJSIpKSANCiAgDQogIHJldHVybihzdHJhdGlmaWVkX25ldXJvX2NvdW50cykNCiAgDQogIA0KfQ0KDQoNCmBgYA0KDQpgYGB7cn0NCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9hZHVsdCA8LSBjb21wdXRlX3N0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZShzdHJhdGlmaWVkX25ldXJvX2NvdW50cyA9IHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FkdWx0LA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhYmxlb25lID0gdGFibGVvbmVfYWR1bHQpDQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9wZWRpYXRyaWMgPC0gY29tcHV0ZV9zdHJhdGlmaWVkX25ldXJvX2NvdW50c19hZ2Uoc3RyYXRpZmllZF9uZXVyb19jb3VudHMgPSBzdHJhdGlmaWVkX25ldXJvX2NvdW50c19wZWRpYXRyaWMsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRhYmxlb25lID0gdGFibGVvbmVfcGVkaWF0cmljKQ0KYGBgDQoNCioqRGlhZ25vc2VzIGJ5IEFkdWx0IGFuZCBQZWRpYXRyaWMgUG9wdWxhdGlvbnMqKg0KDQpgYGB7ciBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9MTV9DQpuXzBfMiA8LSBnZXRfdGFibGVfbih0YWJsZW9uZV9jb21iaW5lLCB2YXIgPSAiMC0yIikNCiNuXzNfNSA8LSBnZXRfdGFibGVfbih0YWJsZW9uZV9jb21iaW5lLCB2YXIgPSAiMy01IikNCm5fNl8xMSA8LSBnZXRfdGFibGVfbih0YWJsZW9uZV9jb21iaW5lLCB2YXIgPSAiNi0xMSIpDQpuXzEyXzE3IDwtIGdldF90YWJsZV9uKHRhYmxlb25lX2NvbWJpbmUsIHZhciA9ICIxMi0xNyIpDQoNCnRvdGFsX3BlZF9hZ2VfY291bnQgPSBhcy5udW1lcmljKG5fMF8yKSArICBhcy5udW1lcmljKG5fNl8xMSkgKyBhcy5udW1lcmljKG5fMTJfMTcpDQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9wZWRpYXRyaWNfcmVmb3JtYXQgPSBzdHJhdGlmaWVkX25ldXJvX2NvdW50c19hZ2VfcGVkaWF0cmljICU+JQ0KICBzZWxlY3QodmFyaWFibGUsIHBuc19jbnMsIGRlc2NyaXB0aW9uLCBuX3ZhciwgdG90YWxfbl92YXIpICU+JQ0KICBtdXRhdGUodmFyaWFibGUgPSAiMC0xNyBZZWFycyIpICU+JQ0KICBncm91cF9ieShwbnNfY25zLCBkZXNjcmlwdGlvbikgJT4lDQogIG11dGF0ZSh0b3RhbF9uX3ZhciA9IHN1bSh0b3RhbF9uX3ZhciwgbmEucm0gPSBUUlVFKSkgJT4lDQogIHVuZ3JvdXAoKSAlPiUNCiAgIyBuX3ZhciBpcyB0aGUgdG90YWwgcGVyIGBhZ2VfZ3JvdXBgIHN0cmF0aWZpY2F0aW9uDQogIHNlbGVjdCgtbl92YXIpICU+JQ0KICBkaXN0aW5jdCgpICU+JQ0KICBtdXRhdGUocGVyYyA9IHJvdW5kKHRvdGFsX25fdmFyL3RvdGFsX3BlZF9hZ2VfY291bnQqMTAwLDEpLA0KICAgICAgICAgcGVyYyA9IGlmX2Vsc2UocGVyYyA8IDAuMSwgJzwwLjEnLCBhcy5jaGFyYWN0ZXIocGVyYykpLA0KICAgICAgICAgcGVyYyA9IHBhc3RlMChwZXJjLCAiJSIpKQ0KDQpzdHJhdGlmaWVkX25ldXJvX2NvdW50c19hZ2VfY29tYmluZWQgPC0gc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWdlX2FkdWx0ICU+JQ0KICBzZWxlY3QodmFyaWFibGUsIHBuc19jbnMsIGRlc2NyaXB0aW9uLCB0b3RhbF9uX3ZhciwgcGVyYykgJT4lDQogIHJiaW5kKC4sIHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9wZWRpYXRyaWNfcmVmb3JtYXQgJT4lDQogICAgICAgICAgc2VsZWN0KHZhcmlhYmxlLCBwbnNfY25zLCBkZXNjcmlwdGlvbiwgdG90YWxfbl92YXIsIHBlcmMpKQ0KDQpzdHJhdGlmaWVkX25ldXJvX2NvdW50c19hZ2VfY29tYmluZWQkdmFyaWFibGUgPC0gZmFjdG9yKHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FnZV9jb21iaW5lZCR2YXJpYWJsZSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHM9YygiMC0xNyBZZWFycyIsICIxOC0yNSBZZWFycyIsICIyNi00OSBZZWFycyIsICI1MC02OSBZZWFycyIsICI3MC03OSBZZWFycyIsICI4MCsgWWVhcnMiKSkNCmBgYA0KDQpgYGB7ciBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9MTJ9DQpncm91cC5jb2xvcnMgPC0gYyhOTkMgPSAiZ3JheSIsIFBOUyA9ICJzbGF0ZWJsdWUiLCBDTlMgPSJ0b21hdG8iKQ0KDQphZHVsdF9wZWRfY29tYmluZWRfcGxvdCA8LSBnZ3Bsb3Qoc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWdlX2NvbWJpbmVkLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSB0b3RhbF9uX3ZhciwgeSA9IHJlb3JkZXIoZGVzY3JpcHRpb24sDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3RhbF9uX3ZhciksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBwbnNfY25zKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBnZW9tX3RleHQoDQogICAgYWVzKHggPSB0b3RhbF9uX3ZhciwgeSA9IGRlc2NyaXB0aW9uLCBsYWJlbCA9IHBlcmMpLA0KICAgIGhqdXN0ID0gLTAuMSwNCiAgICAjaGp1c3Q9Imlud2FyZCIsDQogICAgc2l6ZSA9IDYsDQogICAgaW5oZXJpdC5hZXMgPSBUUlVFKSArDQogIHNjYWxlX3hfY29udGludW91cyhleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwwLjIpKSkgKyAjIHRvIGVuc3VyZSBwZXJjZW50IGxhYmVscyBhcmUgd2l0aGluIHRoZSBmaWd1cmUNCiAgZmFjZXRfZ3JpZChwbnNfY25zIH4gdmFyaWFibGUsIHNjYWxlcyA9ICJmcmVlIiwgc3BhY2UgPSAiZnJlZV95IikgKyANCiAgc2NhbGVfeV9kaXNjcmV0ZSgiIiwgcG9zaXRpb249ImxlZnQiLCBsYWJlbHMgPSBmdW5jdGlvbih4KSBzdHJfd3JhcCh4LCB3aWR0aCA9IDEwMCkpICsgICANCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gZ3JvdXAuY29sb3JzKSArDQogIHhsYWIoIk51bWJlciBvZiBQYXRpZW50cyIpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDQwKSwNCiAgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzNSksDQogICAgICAgIGF4aXMudGV4dC55LmxlZnQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDI1LCBmYWNlID0gImJvbGQiKSwNCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDI1KSwNCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzNSkpDQoNCmdnc2F2ZSgiZmlndXJlcy9GaWczX2RpYWdub3Nlc19ieV9hZ2VfYWR1bHRfcGVkXzE4Z3JvdXAucG5nIiwgYWR1bHRfcGVkX2NvbWJpbmVkX3Bsb3QsIHdpZHRoID0gNDAsIGhlaWdodCA9IDE5LCBkcGkgPSAzMDApDQpgYGANCg0KIVtdKGZpZ3VyZXMvRmlnM19kaWFnbm9zZXNfYnlfYWdlX2FkdWx0X3BlZF8xOGdyb3VwLnBuZyl7I2lkIC5jbGFzcyB3aWR0aD0xMDAwIGhlaWdodD04MDBweH0NCg0KIyMgRXZhbHVhdGUgRGlhZ25vc2VzIGJ5IE91dGNvbWUNCg0KYGBge3J9DQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2FkdWx0JHBvcHVsYXRpb24gPSAiYWR1bHQiDQpzdHJhdGlmaWVkX25ldXJvX2NvdW50c19wZWRpYXRyaWMkcG9wdWxhdGlvbiA9ICJwZWRpYXRyaWMiDQoNCnN0cmF0aWZpZWRfbmV1cm9fY291bnRzX2NvbWJpbmUgPC0gcmJpbmQoc3RyYXRpZmllZF9uZXVyb19jb3VudHNfYWR1bHQsIHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX3BlZGlhdHJpYykNCg0KIyMgZGV0ZXJtaW5lIHRvdGFsIG51bWJlciBvZiBwYXRpZW50cyB3aG8gZGllZCBvciBzZXZlcmUNCm5fZGVhdGhfYWR1bHQgPSBnZXRfdGFibGVfbihkZiA9IHRhYmxlb25lX2FkdWx0LCB2YXIgPSAiRGVjZWFzZWQiKSAlPiUgYXMubnVtZXJpYygpDQpuX3NldmVyZV9hZHVsdCA9IGdldF90YWJsZV9uKGRmID0gdGFibGVvbmVfYWR1bHQsIHZhciA9ICJTZXZlcmUiKSAlPiUgYXMubnVtZXJpYygpDQoNCm5fZGVhdGhfcGVkaWF0cmljID0gZ2V0X3RhYmxlX24oZGYgPSB0YWJsZW9uZV9wZWRpYXRyaWMsIHZhciA9ICJEZWNlYXNlZCIpICU+JSBhcy5udW1lcmljKCkNCm5fc2V2ZXJlX3BlZGlhdHJpYyA9IGdldF90YWJsZV9uKGRmID0gdGFibGVvbmVfcGVkaWF0cmljLCB2YXIgPSAiU2V2ZXJlIikgJT4lIGFzLm51bWVyaWMoKQ0KDQpzdHJhdGlmaWVkX25ldXJvX2NvdW50c19vdXRjb21lcyA8LSBzdHJhdGlmaWVkX25ldXJvX2NvdW50c19jb21iaW5lICU+JSANCiAgZmlsdGVyKHZhcmlhYmxlICVpbiUgYygiU2V2ZXJlIiwgIkRlY2Vhc2VkIiksDQogICAgICAgICAhY29kZSA9PSAiTk4iLCANCiAgICAgICAgICF0b3RhbF9uX3ZhciA9PSAwKSAlPiUgICAgICAgICAgDQogICAgICAgICAjIHRvdGFsX24gd291bGQgYmUgY2FsY3VsYXRpbmcgdG90YWwgY29kZXMgYWNyb3NzIGVhY2ggb3V0Y29tZSBncm91cCB3aGljaCBpcyBub3QgdHJ1ZSByZWZsZWN0aW9uIG9mIHBhdGllbnQgY291bnRzIGIvYyBwYXRpZW50cyBjYW4gaGF2ZSBtb3JlIHRoYW4gb25lIGNvZGUNCiAgICAgICAgICNtdXRhdGUodG90YWxfbiA9IHN1bSh0b3RhbF9uX3ZhcikpICU+JSANCiAgICAgICAgIGdyb3VwX2J5KHZhcmlhYmxlLCBkZXNjcmlwdGlvbiwgcG9wdWxhdGlvbikgJT4lIA0KICBtdXRhdGUodG90YWxfbl92YXIgPSBzdW0odG90YWxfbl92YXIpKSAlPiUgDQogIHVuZ3JvdXAoKSAlPiUgDQogIG11dGF0ZShwZXJjID0gY2FzZV93aGVuKHZhcmlhYmxlID09ICJTZXZlcmUiICYgcG9wdWxhdGlvbiA9PSAiYWR1bHQiIH4gcm91bmQodG90YWxfbl92YXIvbl9zZXZlcmVfYWR1bHQqMTAwLDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPT0gIkRlY2Vhc2VkIiAmIHBvcHVsYXRpb24gPT0gImFkdWx0IiB+IHJvdW5kKHRvdGFsX25fdmFyL25fZGVhdGhfYWR1bHQqMTAwLDEpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyaWFibGUgPT0gIlNldmVyZSIgJiBwb3B1bGF0aW9uID09ICJwZWRpYXRyaWMiIH4gcm91bmQodG90YWxfbl92YXIvbl9zZXZlcmVfcGVkaWF0cmljKjEwMCwxKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlID09ICJEZWNlYXNlZCIgJiBwb3B1bGF0aW9uID09ICJwZWRpYXRyaWMiIH4gcm91bmQodG90YWxfbl92YXIvbl9kZWF0aF9wZWRpYXRyaWMqMTAwLDEpKSwNCiAgICAgICAgIHBlcmMgPSBpZl9lbHNlKHBlcmMgPCAwLjEsICc8MC4xJywgYXMuY2hhcmFjdGVyKHBlcmMpKSwNCiAgICAgICBwZXJjID0gcGFzdGUwKHBlcmMsICIlIikpICU+JSANCiAgc2VsZWN0KC1jb2RlLCAtY29uY2VwdF90eXBlKSAlPiUgDQogIGRpc3RpbmN0KCkNCmBgYA0KDQpgYGB7cn0NCmFkdWx0X291dGNvbWVzID0gZ2dwbG90KHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX291dGNvbWVzICU+JSANCiAgICAgICAgIGZpbHRlcihwb3B1bGF0aW9uID09ICJhZHVsdCIpICU+JSANCiAgICAgICAgICAgbXV0YXRlKHZhcmlhYmxlID0gYXMuZmFjdG9yKHZhcmlhYmxlKSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmY3RfcmVjb2RlKA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNb3J0YWxpdHkgPSAiRGVjZWFzZWQiKSksDQogICAgICAgYWVzKHggPSB0b3RhbF9uX3ZhciwgeSA9IHJlb3JkZXIoZGVzY3JpcHRpb24sIHRvdGFsX25fdmFyKSwgZmlsbCA9IHBuc19jbnMpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogZ2VvbV90ZXh0KA0KICAgIGFlcyh4ID0gdG90YWxfbl92YXIsIHkgPSBkZXNjcmlwdGlvbiwgbGFiZWwgPSBwZXJjKSwNCiAgICBoanVzdCA9IC0wLjEsDQogICAgI2hqdXN0PSJpbndhcmQiLA0KICAgIHNpemUgPSA1LA0KICAgIGluaGVyaXQuYWVzID0gVFJVRSkgKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsMC4yKSkpICsgDQogIGZhY2V0X2dyaWQocG5zX2NucyB+IHZhcmlhYmxlLCBzY2FsZXMgPSAiZnJlZSIsIHNwYWNlID0gImZyZWVfeSIpICsgDQogIHNjYWxlX3lfZGlzY3JldGUoIiIsIHBvc2l0aW9uPSJsZWZ0IiwgbGFiZWxzID0gZnVuY3Rpb24oeCkgc3RyX3dyYXAoeCwgd2lkdGggPSAxMDApKSArICAgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGdyb3VwLmNvbG9ycykgKw0KICB4bGFiKCJOdW1iZXIgb2YgUGF0aWVudHMiKSArIA0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwNCiAgICAgICAgc3RyaXAudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzNSksDQogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzApLA0KICAgICAgICBheGlzLnRleHQueS5sZWZ0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCwgZmFjZSA9ICJib2xkIiksDQogICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSksDQogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfdGV4dChzaXplID0gMzUpKQ0KDQpnZ3NhdmUoImZpZ3VyZXMvU3VwcEZpZzFfZGlhZ25vc2VzX2J5X291dGNvbWVzX2FkdWx0LnBuZyIsIGFkdWx0X291dGNvbWVzLCB3aWR0aCA9IDI1LCBoZWlnaHQgPSAxNSwgZHBpID0gMzAwKQ0KYGBgDQoNCmBgYHtyfQ0KcGVkaWF0cmljX291dGNvbWVzID0gZ2dwbG90KHN0cmF0aWZpZWRfbmV1cm9fY291bnRzX291dGNvbWVzICU+JSANCiAgICAgICAgIGZpbHRlcihwb3B1bGF0aW9uID09ICJwZWRpYXRyaWMiKSAlPiUgDQogICAgICAgICAgIG11dGF0ZSh2YXJpYWJsZSA9IGFzLmZhY3Rvcih2YXJpYWJsZSkgJT4lIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmN0X3JlY29kZSgNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTW9ydGFsaXR5ID0gIkRlY2Vhc2VkIikpLA0KICAgICAgIGFlcyh4ID0gdG90YWxfbl92YXIsIHkgPSByZW9yZGVyKGRlc2NyaXB0aW9uLCB0b3RhbF9uX3ZhciksIGZpbGwgPSBwbnNfY25zKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KIGdlb21fdGV4dCgNCiAgICBhZXMoeCA9IHRvdGFsX25fdmFyLCB5ID0gZGVzY3JpcHRpb24sIGxhYmVsID0gcGVyYyksDQogICAgaGp1c3QgPSAtMC4xLA0KICAgICNoanVzdD0iaW53YXJkIiwNCiAgICBzaXplID0gNSwNCiAgICBpbmhlcml0LmFlcyA9IFRSVUUpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLDAuMikpKSArIA0KICBmYWNldF9ncmlkKHBuc19jbnMgfiB2YXJpYWJsZSwgc2NhbGVzID0gImZyZWUiKSArIA0KICBzY2FsZV95X2Rpc2NyZXRlKCIiLCBwb3NpdGlvbj0ibGVmdCIsIGxhYmVscyA9IGZ1bmN0aW9uKHgpIHN0cl93cmFwKHgsIHdpZHRoID0gMTAwKSkgKyAgIA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBncm91cC5jb2xvcnMpICsNCiAgeGxhYigiIikgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLA0KICAgICAgICBzdHJpcC50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDM1KSwNCiAgICAgICAgc3RyaXAudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzMCksDQogICAgICAgIGF4aXMudGV4dC55LmxlZnQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwLCBmYWNlID0gImJvbGQiKSwNCiAgICAgICAgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSwNCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAzNSkpDQoNCmdnc2F2ZSgiZmlndXJlcy9TdXBwRmlnMV9kaWFnbm9zZXNfYnlfb3V0Y29tZXNfcGVkaWF0cmljLnBuZyIsIHBlZGlhdHJpY19vdXRjb21lcywgd2lkdGggPSAyNSwgaGVpZ2h0ID0gOSwgZHBpID0gMzAwKQ0KYGBgDQoNCg0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1UUlVFfQ0KI2dyaWQuYXJyYW5nZShwZWRpYXRyaWNfb3V0Y29tZXMsIGFkdWx0X291dGNvbWVzLCBuY29sPTEpDQoNCmdnc2F2ZSgiZmlndXJlcy9TdXBwRmlnMV9kaWFnbm9zZXNfYnlfb3V0Y29tZV9jb21iaW5lZC5wbmciLCBncmlkLmFycmFuZ2UocGVkaWF0cmljX291dGNvbWVzLCBhZHVsdF9vdXRjb21lcywgbmNvbD0xKSwgd2lkdGggPSAyNSwgaGVpZ2h0ID0gMjUsIGRwaSA9IDMwMCkNCmBgYA0KDQohW10oZmlndXJlcy9TdXBwRmlnMV9kaWFnbm9zZXNfYnlfb3V0Y29tZV9jb21iaW5lZC5wbmcpeyNpZCAuY2xhc3Mgd2lkdGg9ODAwIGhlaWdodD04MDBweH0NCg0KIyMgRXZhbHVhdGUgcGF0aWVudHMgd2l0aCAiQm90aCIgQ05TIGFuZCBQTlMgZGlzZWFzZXMNCg0KVGhlc2UgcGF0aWVudHMgd2VyZSBleGNsdWRlZCBmcm9tIHRoZSBwcmltYXJ5IGFuYWx5c2lzIGR1ZSB0byBwb3RlbnRpYWwgY29uZm91bmRpbmcuDQoNCmBgYHtyfQ0KIyBjcmVhdGUgZW1wdHkgbGlzdCB0byBzdG9yZSB0aGUgY291bnRzIG9mICJib3RoIiBwYXRpZW50cw0KYm90aF9jb3VudHNfbGlzdCA8LSBsaXN0KCkNCg0KIyBmb3IgZWFjaCBoZWFsdGhjYXJlIHN5c3RlbSwgcmV0cmlldmUgdGhlIG51bWJlciBvZiBleGNsdWRlZCAiYm90aCIgcGF0aWVudHMgcmVjb3JkZWQNCmZvciAoaSBpbiBzb3J0ZWRfc2l0ZXMpIHsNCiAgbl9ib3RoIDwtIHJlc3VsdHNbW3Bhc3RlKGkpXV1bWyJmaXJzdF9ob3NwX3Jlc3VsdHMiXV1bWyJib3RoX2NvdW50cyJdXQ0KICANCiAgYm90aF9jb3VudHNfbGlzdFtbaV1dIDwtIG5fYm90aA0KfQ0KDQpib3RoX2NvdW50cyA8LSBiaW5kX3Jvd3MoYm90aF9jb3VudHNfbGlzdCkgJT4lIHQoKSAlPiUgZGF0YS5mcmFtZSgpICU+JSBzdW0oKQ0KDQpwcmludChwYXN0ZShib3RoX2NvdW50cywgIigiLCByb3VuZChib3RoX2NvdW50cy8oYXMubnVtZXJpYyh0YWJsZW9uZV9jb21iaW5lJE5bMV0pK2JvdGhfY291bnRzKSoxMDAsMiksICIlKSIpKSANCmBgYA0KDQojICoqQ29tb3JiaWRpdHkgQW5hbHlzaXMqKg0KDQpDcmVhdGUgY29tb3JiaWRpdHkgY291bnQgdGFibGUNCg0KYGBge3J9DQpjb21vcmJpZGl0eV90YWJsZV9hZHVsdCA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUNCiAgc2VsZWN0KENvbW9yYmlkaXR5LCBwb3B1bGF0aW9uLCBDb21vcmJfVG90YWwsIGBOTkNfTl8lYCwgYENOU19OXyVgLCBgUE5TX05fJWApICU+JQ0KICBmaWx0ZXIocG9wdWxhdGlvbiA9PSAiQWR1bHQiKSAlPiUgDQogIHJlbmFtZShOID0gIkNvbW9yYl9Ub3RhbCIsDQogICAgICAgICBOTkMgPSAiTk5DX05fJSIsDQogICAgICAgICBDTlMgPSAiQ05TX05fJSIsDQogICAgICAgICBQTlMgPSAiUE5TX05fJSIpICU+JSANCiAgbXV0YXRlKE4gPSBwYXN0ZTAoTiwgIiAoIiwgcm91bmQoTi9uX2FkdWx0LCAyKSoxMDAsICIlKSIpKQ0KDQpjb21vcmJpZGl0eV90YWJsZV9wZWRpYXRyaWMgPC0gY29tb3JiX3RhYmxlX3dpZGUgJT4lDQogIHNlbGVjdChDb21vcmJpZGl0eSwgcG9wdWxhdGlvbiwgQ29tb3JiX1RvdGFsLCBgTk5DX05fJWAsIGBDTlNfTl8lYCwgYFBOU19OXyVgKSAlPiUNCiAgZmlsdGVyKHBvcHVsYXRpb24gPT0gIlBlZGlhdHJpYyIpICU+JSANCiAgcmVuYW1lKE4gPSAiQ29tb3JiX1RvdGFsIiwNCiAgICAgICAgIE5OQyA9ICJOTkNfTl8lIiwNCiAgICAgICAgIENOUyA9ICJDTlNfTl8lIiwNCiAgICAgICAgIFBOUyA9ICJQTlNfTl8lIikgJT4lIA0KICBtdXRhdGUoTiA9IHBhc3RlMChOLCAiICgiLCByb3VuZChOL25fcGVkaWF0cmljLCAyKSoxMDAsICIlKSIpKQ0KDQpzdXBwX2NvbW9yYmlkaXR5X3RhYmxlIDwtIHJiaW5kKGNvbW9yYmlkaXR5X3RhYmxlX2FkdWx0LCBjb21vcmJpZGl0eV90YWJsZV9wZWRpYXRyaWMpICU+JSANCiAgYXJyYW5nZShDb21vcmJpZGl0eSkNCg0Kd3JpdGUuY3N2KHN1cHBfY29tb3JiaWRpdHlfdGFibGUsICJ0YWJsZXMvU3VwcFRhYmxlX0NvbW9yYmlkaXR5X0NvdW50cy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCmBgYA0KDQpDcmVhdGUgdmVjdG9yIGNvdW50cyBvZiBwYXRpZW50cyB3aXRoIGNvbW9yYmlkaXR5IGRhdGEgDQoNCmBgYHtyfQ0KbmV1cm9fcHRfY291bnRzX3N1bV9hZHVsdCA8LSBuZXVyb19wdF9jb3VudHMgJT4lIA0KICBmaWx0ZXIocG9wdWxhdGlvbiA9PSAiQWR1bHQiKSAlPiUgDQogIHNlbGVjdCgtcG9wdWxhdGlvbikNCg0KbmV1cm9fcHRfY291bnRzX3N1bV9wZWRpYXRyaWMgPC0gbmV1cm9fcHRfY291bnRzICU+JSANCiAgZmlsdGVyKHBvcHVsYXRpb24gPT0gIlBlZGlhdHJpYyIpICU+JSANCiAgc2VsZWN0KC1wb3B1bGF0aW9uKQ0KDQpub25lX25fYWR1bHQgPSBuZXVyb19wdF9jb3VudHNfc3VtX2FkdWx0JE5vbmVfbg0KY25zX25fYWR1bHQgPSBuZXVyb19wdF9jb3VudHNfc3VtX2FkdWx0JENOU19uDQpwbnNfbl9hZHVsdCA9IG5ldXJvX3B0X2NvdW50c19zdW1fYWR1bHQkUE5TX24NCg0Kbm9uZV9uX3BlZGlhdHJpYyA9IG5ldXJvX3B0X2NvdW50c19zdW1fcGVkaWF0cmljJE5vbmVfbg0KY25zX25fcGVkaWF0cmljID0gbmV1cm9fcHRfY291bnRzX3N1bV9wZWRpYXRyaWMkQ05TX24NCnBuc19uX3BlZGlhdHJpYyA9IG5ldXJvX3B0X2NvdW50c19zdW1fcGVkaWF0cmljJFBOU19uDQpgYGANCg0KIyMjIENhbGN1bGF0ZSBSZWxhdGl2ZSBSaXNrIChSUikNCg0KYGBge3J9DQpjYWxjdWxhdGVfcnIgPC0gZnVuY3Rpb24oaXNfcGVkaWF0cmljID0gRkFMU0UpIHsNCiAgDQogIGlmKGlzX3BlZGlhdHJpYyA9PSBGQUxTRSkgew0KICAgIGNuc19uID0gY25zX25fYWR1bHQNCiAgICBwbnNfbiA9IHBuc19uX2FkdWx0DQogICAgbmV1cm9fcHRfY291bnRzX3N1bSA9IG5ldXJvX3B0X2NvdW50c19zdW1fYWR1bHQNCiAgICBwb3AgPSAiQWR1bHQiDQogICAgIyBjcmVhdGUgYSBsaXN0IG9mIGNvbW9yYmlkaXRpZXMNCiAgICBsaXN0X29mX2NvbW9yYnMgPC0gY29tb3JiX3RhYmxlX3dpZGUgJT4lIA0KICAgICAgZmlsdGVyKHBvcHVsYXRpb24gPT0gcGFzdGUocG9wKSkgJT4lIA0KICAgICAgZGlzdGluY3QoQ29tb3JiaWRpdHkpIA0KICAgIGxpc3Rfb2ZfY29tb3JicyA8LSBsaXN0X29mX2NvbW9yYnMkQ29tb3JiaWRpdHkNCiAgICB9IGVsc2UgIHsNCiAgICBjbnNfbiA9IGNuc19uX3BlZGlhdHJpYw0KICAgIHBuc19uID0gcG5zX25fcGVkaWF0cmljDQogICAgbmV1cm9fcHRfY291bnRzX3N1bSA9IG5ldXJvX3B0X2NvdW50c19zdW1fcGVkaWF0cmljDQogICAgcG9wID0gIlBlZGlhdHJpYyINCiAgICAjIGNyZWF0ZSBhIGxpc3Qgb2YgY29tb3JiaWRpdGllcw0KICAgIGxpc3Rfb2ZfY29tb3JicyA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUgDQogICAgICBmaWx0ZXIocG9wdWxhdGlvbiA9PSBwYXN0ZShwb3ApKSAlPiUgDQogICAgICBkaXN0aW5jdChDb21vcmJpZGl0eSkgJT4lIA0KICAgICAgYXMudmVjdG9yKCkNCiAgICBsaXN0X29mX2NvbW9yYnMgPC0gbGlzdF9vZl9jb21vcmJzJENvbW9yYmlkaXR5DQogIH0NCiAgDQogIA0KICAjIGNyZWF0ZSBlbXB0eSBsaXN0IHRvIHNhdmUgcmVsYXRpdmUgcmlzayBjYWxjdWxhdGlvbnMNCiAgcnJfY2FsY3MgPC0gbGlzdCgpDQogIA0KICAjIGZvciBlYWNoIGNvbW9yYmlkaXR5LCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgcmVsYXRpdmUgcmlzayBvZiBoYXZpbmcgYSBDTlMgYW5kIFBOUyBkaWFnbm9zaXMNCiAgZm9yIChpIGluIGxpc3Rfb2ZfY29tb3Jicykgew0KICANCiAgICAjIGh0dHBzOi8vd3d3LmNkYy5nb3YvY3NlbHMvZHNlcGQvc3MxOTc4L2xlc3NvbjMvc2VjdGlvbjUuaHRtbA0KICAgICMgY3JlYXRlIG1hdHJpeCBhczoNCiAgICAjIGV4cG9zZWQgZ3JvdXAgLSB3aXRoIG91dGNvbWUsIHdpdGhvdXQgb3V0Y29tZQ0KICAgICMgbm9uIGV4cG9zZWQgZ3JvdXAgLSB3aXRoIG91dGNvbWUsIHdpdGhvdXQgb3V0Y29tZQ0KICANCiAgICAjIHdoZXJlIHZhcmlhYmxlcyBlbmRpbmcgd2l0aCBgX1RvdGFsYCBhcmUgdGhlIHRvdGFsIG51bWJlciBvZiBDTlMsIFBOUywgb3IgTk5DIHBhdGllbnRzIHdpdGggdGhlIHJlc3BlY3RpdmUgY29tb3JiaWRpdHkuDQogICAgIyB2YXJpYWJsZXMgZW5kaW5nIHdpdGggYF9uYCBhcmUgdGhlIHRvdGFsIG51bWJlcnMgaW4gdGhlIGVudGlyZSBwb3B1bGF0aW9uDQogIA0KICAgICMgdG8gY2FsY3VsYXRlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzDQogICNodHRwczovL3NwaHdlYi5idW1jLmJ1LmVkdS9vdGx0L21waC1tb2R1bGVzL2JzL2JzNzA0X2NvbmZpZGVuY2VfaW50ZXJ2YWxzL2JzNzA0X2NvbmZpZGVuY2VfaW50ZXJ2YWxzOC5odG1sDQogIA0KICAgIHJyX2NucyA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUNCiAgICAgIGZpbHRlcihwb3B1bGF0aW9uID09IHBhc3RlKHBvcCksDQogICAgICAgICAgICAgQ29tb3JiaWRpdHkgPT0gcGFzdGUoaSkpICU+JQ0KICAgICAgbXV0YXRlKGEgPSBDTlNfVG90YWwsICMgbnVtYmVyIENOUyB3L2NvbW9yYg0KICAgICAgICAgICAgIGIgPSBDb21vcmJfVG90YWwgLSBDTlNfVG90YWwsICMgbnVtYmVyIG9mIGNvbW9yYiBwdHMgdy9vIENOUw0KICAgICAgICAgICAgIGMgPSBjbnNfbiAtIENOU19Ub3RhbCwgIyBudW1iZXIgb2YgQ05TIHB0cyB3L28gY29tb3JiDQogICAgICAgICAgICAgZCA9IHN1bShuZXVyb19wdF9jb3VudHNfc3VtKSAtIENvbW9yYl9Ub3RhbCAtIGMsICMgbnVtYmVyIG9mIHB0cyB3L28gY29tb3JiIG9yIENOUw0KICAgICAgICAgICAgIHJpc2tfY25zID0gYS8oYStiKSwNCiAgICAgICAgICAgICByaXNrX25vbl9jbnMgPSBjLyhjK2QpLA0KICAgICAgICAgICAgIHJyX0NOUyA9IHJpc2tfY25zL3Jpc2tfbm9uX2NucywNCiAgICAgICAgICAgICAjIGZvciBjb25maWRlbmNlIGludGVydmFscw0KICAgICAgICAgICAgIGNpXzEgPSAoYi9hKS8oYStiKSwgIyAobjEteDEpL24xIA0KICAgICAgICAgICAgIGNpXzIgPSAoZC9jKS8oYytkKSwgIyAobjIteDIpL24yDQogICAgICAgICAgICAgY2lfcm9vdCA9IHNxcnQoY2lfMSArIGNpXzIpLA0KICAgICAgICAgICAgIGNpX2xvd2VyID0gbG9nKHJyX0NOUykgLSAoMS45NipjaV9yb290KSwNCiAgICAgICAgICAgICBjaV91cHBlciA9IGxvZyhycl9DTlMpICsgKDEuOTYqY2lfcm9vdCksDQogICAgICAgICAgICAgbF9jaV9jbnMgPSBleHAoY2lfbG93ZXIpLA0KICAgICAgICAgICAgIHVfY2lfY25zID0gZXhwKGNpX3VwcGVyKSkgJT4lDQogICAgICBzZWxlY3QoQ29tb3JiaWRpdHksIENvbW9yYl9Ub3RhbCwgTm9uZV9Ub3RhbCwgQ05TX1RvdGFsLCBQTlNfVG90YWwsIHJyX0NOUywgbF9jaV9jbnMsIHVfY2lfY25zKQ0KICANCiAgICAgIHJyX3BucyA8LSBjb21vcmJfdGFibGVfd2lkZSAlPiUNCiAgICAgIGZpbHRlcihwb3B1bGF0aW9uID09IHBhc3RlKHBvcCksDQogICAgICAgICAgICAgQ29tb3JiaWRpdHkgPT0gcGFzdGUoaSkpICU+JQ0KICAgICAgbXV0YXRlKGEgPSBQTlNfVG90YWwsICMgbnVtYmVyIFBOUyB3L2NvbW9yYg0KICAgICAgICAgICAgIGIgPSBDb21vcmJfVG90YWwgLSBQTlNfVG90YWwsICMgbnVtYmVyIG9mIGNvbW9yYiBwdHMgdy9vIFBOUw0KICAgICAgICAgICAgIGMgPSBwbnNfbiAtIFBOU19Ub3RhbCwgIyBudW1iZXIgb2YgUE5TIHB0cyB3L28gY29tb3JiDQogICAgICAgICAgICAgZCA9IHN1bShuZXVyb19wdF9jb3VudHNfc3VtKSAtIENvbW9yYl9Ub3RhbCAtIGMsICMgbnVtYmVyIG9mIHB0cyB3L28gY29tb3JiIG9yIFBOUw0KICAgICAgICAgICAgIHJpc2tfcG5zID0gYS8oYStiKSwNCiAgICAgICAgICAgICByaXNrX25vbl9wbnMgPSBjLyhjK2QpLA0KICAgICAgICAgICAgIHJyX1BOUyA9IHJpc2tfcG5zL3Jpc2tfbm9uX3BucywNCiAgICAgICAgICAgICAjIGZvciBjb25maWRlbmNlIGludGVydmFscw0KICAgICAgICAgICAgIGNpXzEgPSAoYi9hKS8oYStiKSwNCiAgICAgICAgICAgICBjaV8yID0gKGQvYykvKGMrZCksDQogICAgICAgICAgICAgY2lfcm9vdCA9IHNxcnQoY2lfMSArIGNpXzIpLA0KICAgICAgICAgICAgIGNpX2xvd2VyID0gbG9nKHJyX1BOUykgLSAoMS45NipjaV9yb290KSwNCiAgICAgICAgICAgICBjaV91cHBlciA9IGxvZyhycl9QTlMpICsgKDEuOTYqY2lfcm9vdCksDQogICAgICAgICAgICAgbF9jaV9wbnMgPSBleHAoY2lfbG93ZXIpLA0KICAgICAgICAgICAgIHVfY2lfcG5zID0gZXhwKGNpX3VwcGVyKSkgJT4lDQogICAgICBzZWxlY3QoQ29tb3JiaWRpdHksIHJyX1BOUywgbF9jaV9wbnMsIHVfY2lfcG5zKQ0KICANCiAgICAgIHJyIDwtIHJyX2NucyAlPiUNCiAgICAgICAgbGVmdF9qb2luKC4sIHJyX3BucywgYnkgPSBjKCJDb21vcmJpZGl0eSIpKQ0KICANCiAgICBycl9jYWxjc1tbaV1dIDwtIHJyDQogIA0KICB9DQogIA0KICBycl9yZXN1bHRzIDwtIGJpbmRfcm93cyhycl9jYWxjcykgJT4lDQogICAgICBtdXRhdGUocnJfQ05TID0gcm91bmQocnJfQ05TLCAyKSwNCiAgICAgICAgICAgbF9jaV9jbnMgPSByb3VuZChsX2NpX2NucywgMiksDQogICAgICAgICAgIHVfY2lfY25zID0gcm91bmQodV9jaV9jbnMsIDIpLA0KICAgICAgICAgICBycl9QTlMgPSByb3VuZChycl9QTlMsIDIpLA0KICAgICAgICAgICBsX2NpX3BucyA9IHJvdW5kKGxfY2lfcG5zLCAyKSwNCiAgICAgICAgICAgdV9jaV9wbnMgPSByb3VuZCh1X2NpX3BucywgMiksDQogICAgICAgICAgIGBSUiBDTlMgKDk1JSBDSSlgID0gcGFzdGUocnJfQ05TLCAiKCIsIGxfY2lfY25zLCAiLCIsIHVfY2lfY25zLCAiKSIpLA0KICAgICAgICAgICBgUlIgUE5TICg5NSUgQ0kpYCA9IHBhc3RlKHJyX1BOUywgIigiLCBsX2NpX3BucywgIiwiLCB1X2NpX3BucywgIikiKSkgJT4lDQogICAgc2VsZWN0KENvbW9yYmlkaXR5LCBycl9DTlMsIGxfY2lfY25zLCB1X2NpX2NucywgYFJSIENOUyAoOTUlIENJKWAsDQogICAgICAgICAgIHJyX1BOUywgbF9jaV9wbnMsIHVfY2lfcG5zLCBgUlIgUE5TICg5NSUgQ0kpYCkNCiAgDQogICMgdGlkeSB1cCBkYXRhDQogIHdyaXRlLmNzdihycl9yZXN1bHRzICU+JQ0KICAgICAgICAgICAgICBhcnJhbmdlKGRlc2MocnJfQ05TKSkgJT4lDQogICAgICAgICAgICAgIHNlbGVjdChDb21vcmJpZGl0eSwgYFJSIENOUyAoOTUlIENJKWAsIGBSUiBQTlMgKDk1JSBDSSlgKSwgDQogICAgICAgICAgICBwYXN0ZTAoInRhYmxlcy9TdXBwLlRhYmxlNF9SUl8iLCBwb3AsICIuY3N2IiksIHJvdy5uYW1lcyA9IEZBTFNFKQ0KICANCiAgICAjIHJlZm9ybWF0IGRhdGENCiAgcnJfcmVzdWx0c190aWR5X3JyIDwtIHJyX3Jlc3VsdHMgJT4lDQogICAgc2VsZWN0KENvbW9yYmlkaXR5LCBycl9DTlMsIHJyX1BOUykgJT4lDQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBycl9DTlM6cnJfUE5TLCBuYW1lc190byA9ICJOZXVybyBTdGF0dXMiLCB2YWx1ZXNfdG8gPSAiUlIiKSAlPiUNCiAgICBtdXRhdGUoYE5ldXJvIFN0YXR1c2AgPSBpZl9lbHNlKGBOZXVybyBTdGF0dXNgID09ICJycl9DTlMiLCAiQ05TIiwgIlBOUyIpKQ0KICANCiAgcnJfcmVzdWx0c190aWR5X2xfY2kgPC0gcnJfcmVzdWx0cyAlPiUNCiAgICBzZWxlY3QoQ29tb3JiaWRpdHksIGxfY2lfY25zLCBsX2NpX3BucykgJT4lDQogICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBsX2NpX2NuczpsX2NpX3BucywgbmFtZXNfdG8gPSAiTmV1cm8gU3RhdHVzIiwgdmFsdWVzX3RvID0gImxfQ0kiKSAlPiUNCiAgICBtdXRhdGUoYE5ldXJvIFN0YXR1c2AgPSBpZl9lbHNlKGBOZXVybyBTdGF0dXNgID09ICJsX2NpX2NucyIsICJDTlMiLCAiUE5TIikpDQogIA0KICBycl9yZXN1bHRzX3RpZHlfdV9jaSA8LSBycl9yZXN1bHRzICU+JQ0KICAgIHNlbGVjdChDb21vcmJpZGl0eSwgdV9jaV9jbnMsIHVfY2lfcG5zKSAlPiUNCiAgICBwaXZvdF9sb25nZXIoY29scyA9IHVfY2lfY25zOnVfY2lfcG5zLCBuYW1lc190byA9ICJOZXVybyBTdGF0dXMiLCB2YWx1ZXNfdG8gPSAidV9DSSIpICU+JQ0KICAgIG11dGF0ZShgTmV1cm8gU3RhdHVzYCA9IGlmX2Vsc2UoYE5ldXJvIFN0YXR1c2AgPT0gInVfY2lfY25zIiwgIkNOUyIsICJQTlMiKSkNCiAgDQogICMgY29tYmluZSBhbGwgdGlkeSByZXN1bHRzDQogIHJyX3Jlc3VsdHNfdGlkeSA8LSBycl9yZXN1bHRzX3RpZHlfcnIgJT4lDQogICAgbGVmdF9qb2luKC4sIHJyX3Jlc3VsdHNfdGlkeV9sX2NpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiQ29tb3JiaWRpdHkiLCAiTmV1cm8gU3RhdHVzIikpICU+JQ0KICAgIGxlZnRfam9pbiguLCBycl9yZXN1bHRzX3RpZHlfdV9jaSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIkNvbW9yYmlkaXR5IiwgIk5ldXJvIFN0YXR1cyIpKQ0KICANCiAgIyBvcmRlciBieSBDTlMgZGlhZ25vc2VzIHdpdGggaGlnaGVyIFJSDQogIGNuc190b3BfcmVzdWx0cyA8LSBycl9yZXN1bHRzX3RpZHkgJT4lDQogICAgZmlsdGVyKGBOZXVybyBTdGF0dXNgID09ICJDTlMiKSAlPiUNCiAgICBhcnJhbmdlKFJSKSAlPiUNCiAgICBzZWxlY3QoQ29tb3JiaWRpdHkpDQogIA0KICBycl9yZXN1bHRzX3RpZHkkQ29tb3JiaWRpdHkgPC0gIG9yZGVyZWQocnJfcmVzdWx0c190aWR5JENvbW9yYmlkaXR5LCBsZXZlbHMgPSBjbnNfdG9wX3Jlc3VsdHMkQ29tb3JiaWRpdHkpDQogIA0KICBjbnNfcnJfcGxvdCA8LSBnZ3Bsb3QocnJfcmVzdWx0c190aWR5ICU+JQ0KICAgICAgICAgICBmaWx0ZXIoYE5ldXJvIFN0YXR1c2AgPT0gIkNOUyIpLA0KICAgICAgICAgYWVzKHggPSBSUiwgeSA9IENvbW9yYmlkaXR5KSkgKw0KICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAxKSwgc2l6ZSA9IC4yNSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICAgICAgZ2VvbV9lcnJvcmJhcmgoYWVzKHhtYXggPSB1X0NJLCB4bWluID0gbF9DSSksIHNpemUgPSAwLjcsIGhlaWdodCA9IDAuMiwgY29sb3IgPSAiZ3JheTUwIikgKw0KICAgICAgZ2VvbV9wb2ludChzaXplID0gMS41LCBjb2xvciA9ICJ0b21hdG8iKSArDQogICAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDQsIDEpLCBsYWJlbHMgPSBzZXEoMCwgNCwgMSksDQogICAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLDQpKSArDQogICAgeWxhYigiIikgKw0KICAgIHhsYWIoIlJlbGF0aXZlIFJpc2siKSArDQogICAgZ2d0aXRsZSgiQ05TIFBhdGllbnRzIikgKyANCiAgICB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChmYWNlID0gImJvbGQiKSkNCiAgDQogICMgYXJyYW5nZSBieSB0b3AgUE5TIHJlc3VsdHMNCiAgcG5zX3RvcF9yZXN1bHRzIDwtIHJyX3Jlc3VsdHNfdGlkeSAlPiUNCiAgICBmaWx0ZXIoYE5ldXJvIFN0YXR1c2AgPT0gIlBOUyIpICU+JQ0KICAgIGFycmFuZ2UoUlIpICU+JQ0KICAgIHNlbGVjdChDb21vcmJpZGl0eSkNCiAgDQogIHJyX3Jlc3VsdHNfdGlkeSRDb21vcmJpZGl0eSA8LSAgb3JkZXJlZChycl9yZXN1bHRzX3RpZHkkQ29tb3JiaWRpdHksIGxldmVscyA9IHBuc190b3BfcmVzdWx0cyRDb21vcmJpZGl0eSkNCiAgDQogIA0KICBwbnNfcnJfcGxvdCA8LSBnZ3Bsb3QocnJfcmVzdWx0c190aWR5ICU+JQ0KICAgICAgICAgICBmaWx0ZXIoYE5ldXJvIFN0YXR1c2AgPT0gIlBOUyIpLA0KICAgICAgICAgYWVzKHggPSBSUiwgeSA9IENvbW9yYmlkaXR5KSkgKw0KICAgIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQgPSAxKSwgc2l6ZSA9IC4yNSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKw0KICAgICAgZ2VvbV9lcnJvcmJhcmgoYWVzKHhtYXggPSB1X0NJLCB4bWluID0gbF9DSSksIHNpemUgPSAwLjcwLCBoZWlnaHQgPSAwLjIsIGNvbG9yID0gImdyYXk1MCIpICsNCiAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDEuNSwgY29sb3IgPSAic2xhdGVibHVlIikgKw0KICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxLjUsIDAuNSksIGxhYmVscyA9IHNlcSgwLCAxLjUsIDAuNSksDQogICAgICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLDEuNSkpICsNCiAgICB5bGFiKCIiKSArDQogICAgeGxhYigiUmVsYXRpdmUgUmlzayIpICsNCiAgICBnZ3RpdGxlKCJQTlMgUGF0aWVudHMiKSArIA0KICAgIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KGZhY2UgPSAiYm9sZCIpKQ0KICANCiAgcG5zX3JyX3Bsb3QgPC0gc2V0X3BhbmVsX3NpemUocG5zX3JyX3Bsb3QsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgd2lkdGggID0gdW5pdCg3LCAiY20iKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQgPSB1bml0KDYuNSwgImluIikpDQogIA0KICBjbnNfcnJfcGxvdCA8LSBzZXRfcGFuZWxfc2l6ZShjbnNfcnJfcGxvdCwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB3aWR0aCAgPSB1bml0KDcsICJjbSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlaWdodCA9IHVuaXQoNi41LCAiaW4iKSkNCiAgDQogICNncmlkLmFycmFuZ2UoY25zX3JyX3Bsb3QsIHBuc19ycl9wbG90LCBuY29sPTIpDQogIA0KICBnZ3NhdmUocGFzdGUwKCJmaWd1cmVzL0ZpZ3VyZTQucmVsYXRpdmVfcmlza18iLCBwb3AsICIucG5nIiksIGdyaWQuYXJyYW5nZShjbnNfcnJfcGxvdCwgcG5zX3JyX3Bsb3QsIG5jb2w9MiksIHdpZHRoID0gMTUsIGhlaWdodCA9IDgsIGRwaSA9IDMwMCkNCiAgDQogIH0NCmBgYA0KDQpgYGB7ciBldmFsPVRSVUUsIGluY2x1ZGU9RkFMU0V9DQpycl9hZHVsdCA8LSBjYWxjdWxhdGVfcnIoaXNfcGVkaWF0cmljID0gRkFMU0UpDQojcnJfcGVkaWF0cmljIDwtIGNhbGN1bGF0ZV9ycihpc19wZWRpYXRyaWMgPSBUUlVFKQ0KYGBgDQoNCiFbXShmaWd1cmVzL0ZpZ3VyZTQucmVsYXRpdmVfcmlza19BZHVsdC5wbmcpeyNpZCAuY2xhc3Mgd2lkdGg9MTAwMCBoZWlnaHQ9ODAwcHh9DQoNCg0KIyMjIExQQ0EgZGV2aWFuY2UgZXhwbGFpbmVkDQoNCkhvdyBtdWNoIGRldmlhbmNlIGlzIGV4cGxhaW5lZCB3aXRoIDEwIHByaW5jaXBhbCBjb21wb25lbnRzIGZvciAzMCBjb21vcmJpZGl0eSB0eXBlcz8NCg0KQWNyb3NzIGFsbCBoZWFsdGhjYXJlIHN5c3RlbXMsIDEwIHByaW5jaXBhbCBjb21wb25lbnRzIGV4cGxhaW5lZCBhYm92ZSA3NSUgb2YgdGhlIGRldmlhbmNlLg0KDQpgYGB7cn0NCmNvbXBhcmVfZGV2aWFuY2UgPC0gZnVuY3Rpb24oc29ydGVkX3NpdGVzLCBpc19wZWRpYXRyaWM9RkFMU0UpIHsNCiAgDQogIHBjYV9kZXYgPC0gbGlzdCgpDQogIA0KICBpZihpc19wZWRpYXRyaWM9PUZBTFNFKSB7DQogICAgcG9wdWxhdGlvbiA9ICJhZHVsdHMiDQogICAgc29ydGVkX3NpdGVzID0gc29ydGVkX3NpdGVzWyFzb3J0ZWRfc2l0ZXMlaW4lIGMoIkdPU0hfcmVzdWx0cyIsICJCQ0hfcmVzdWx0cyIpXQ0KICAgIH0gZWxzZSB7DQogICAgcG9wdWxhdGlvbiA9ICJwZWRpYXRyaWNzIg0KICAgIH0NCg0KICAjIGZvciBlYWNoIGhlYWx0aGNhcmUgc3lzdGVtLCB3ZSB3aWxsIGNhbGN1bGF0ZSB0aGUgdG90YWwgbnVtYmVyIG9mIHBhdGllbnRzIHdpdGggZWFjaCBJQ0QgY29kZQ0KICBmb3IgKGkgaW4gc29ydGVkX3NpdGVzKSB7DQogICAgcHJpbnQoaSkNCiAgICB0bXAgPC0gZ2V0KGkpDQogICAgdG1wX3BjYSA8LSB0bXBbW2MoDQogICAgICAiY29tb3JiaWRpdGllcyIsDQogICAgICBwYXN0ZTAoImNvbW9yYl8iLCBwb3B1bGF0aW9uKSwNCiAgICAgICJkZXZpYW5jZV9leHBsIg0KICAgICldXSAlPiUgDQogICAgICBkYXRhLmZyYW1lKCkgJT4lIA0KICAgICAgcmVuYW1lKGRldl9leHBsID0gJy4nKQ0KICAgIHRtcF9wY2Ekc2l0ZSA8LSB0bXBbWyJzaXRlIl1dDQogICAgcGNhX2RldltbaV1dIDwtIHRtcF9wY2ENCiAgICANCiAgfQ0KICANCiAgcGNhX2RmIDwtIGJpbmRfcm93cyhwY2FfZGV2KQ0KICANCiAgcmV0dXJuKHBjYV9kZikNCiAgDQp9DQoNCnBjYV9hZHVsdCA8LSBjb21wYXJlX2RldmlhbmNlKHNvcnRlZF9zaXRlcyA9IGFkdWx0X3NpdGVzLCBpc19wZWRpYXRyaWMgPSBGQUxTRSkNCg0KDQpwY2FfZGF0YSA8LSBiaW5kX3Jvd3MocGNhX2FkdWx0KQ0KDQoNCnRoZW1lX3NldCh0aGVtZV9jbGFzc2ljKCkpDQoNCnBjYV9wbG90IDwtIGdncGxvdChwY2FfZGF0YSwgDQogICAgICAgYWVzKHggPSBmY3RfcmVvcmRlcihzaXRlLCAtZGV2X2V4cGwpLCB5ID0gZGV2X2V4cGwsIGdyb3VwID0gMSkpICsNCiAgZ2VvbV9wb2ludChzaXplID0gMi41KSArDQogIGdlb21fbGluZSgpICsNCiAgeGxhYigiSGVhbHRoY2FyZSBTeXN0ZW0iKSArIA0KICB5bGFiKCJQcm9wb3J0aW9uIG9mIERldmlhbmNlIGV4cGxhaW5lZCIpICsgIA0KICB5bGltKDAsMSkgKyANCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTkwLCBoanVzdD0xLCBmYWNlID0gImJvbGQiLCBzaXplID0gMTIpLA0KICAgICAgICBheGlzLnRleHQueT1lbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiksIA0KICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDE1KSk7IHBjYV9wbG90DQoNCmdnc2F2ZSgiZmlndXJlcy9TdXBwRmlnX3BjYV9kZXZpYW5jZS5wbmciLCBwY2FfcGxvdCwgaGVpZ2h0ID0gNiwgd2lkdGggPSA2KQ0KYGBgDQo=